Ana içeriğe atla

Önbelleğe almak


Önbelleğe alma, modern, yoğun trafikli uygulamaların önemli bir parçasıdır. Web uygulamanız şu anda çok fazla trafik almıyor olsa bile, daha sonra alabilir ve onu en baştan önbelleğe almayı göz önünde bulundurarak tasarlamak iyi bir fikirdir.

.NET, önbelleğe alınmış öğeleri Redis, NCache vb. gibi uzak bir paylaşılan sunucuda depolamak için yerel, bellek içi önbelleğe alma (MemoryCache) ve dağıtılmış önbelleğe alma (IDistribatedCache) için mekanizmalar sağlar:

Serenity, .NET önbellekleme mekanizmalarını temel alır ve yerel ve dağıtılmış önbelleklerle çalışmayı kolaylaştırmak için bazı yardımcı işlevler sağlar.

Yerel terimi, önbelleğe alınmış öğelerin yerel bellekte tutulduğu anlamına gelir (bu nedenle serileştirme söz konusu değildir).

Uygulamanız bir web grubuna dağıtıldığında, bellek içi önbelleğe alınan öğeler birden fazla sunucu arasında paylaşılmadığından yerel önbelleğe alma yeterli olmayabilir veya bazen uygun olmayabilir. Dağıtılmış önbelleklemenin devreye girdiği yer burasıdır.

MemoryCacheExtensions Sınıfı

Statik MemoryCacheExtensions sınıfı MemoryCache, .

Uzantı Get<Item>, önbelleğe alınmış mevcut bir öğeyi alma veya mevcut değilse onu bir fabrika işlevi aracılığıyla başlatma olanağı sağlar. Ayrıca, öğenin önbellekte ne kadar süre saklanacağını (örneğin son kullanma tarihi) belirtmenize de olanak tanır.

memoryCache.Get<MyCachedItemType>("SomeUniqueCacheKey", TimeSpan.FromMinutes(5),
    () => { // this is the factory or loader function
        var item  = new MyCachedItemType();
        // populate item from database etc.
        return item;
    });

Uzatma yöntemimizin akış şeması Get:

MemoryCacheExtensions.Get Akış Diyagramı

GetOrCreate.NET'te de benzer bir yöntem var ancak süre sonu belirtmenize izin vermiyor ve özel bir değere sahip CacheExtensionsbir sonucu önbelleğe almıyor .not foundDBNull

Kullanıcı Profili Önbelleğe Alma Örneği

Sitemizde çeşitli sorgular kullanılarak oluşturulan bir profil sayfamızın olduğunu varsayalım. Bu sayfa için bir modelimiz olabilir, örneğin bir kullanıcının tüm profil verilerini içeren UserProfile sınıfı ve bunu belirli bir kullanıcı kimliği için üreten bir GetProfile yöntemi.

public class UserProfile
{
	public string Name { get; set; }
	public List<CachedFriend> Friends { get; set; }
	public List<CachedAlbum> Albums { get; set; }
	...
}
public UserProfile GetProfile(int userID)
{
	using (var connection = new SqlConnection("..."))
	{
        // load profile by userID from DB
    }
}

LocalCacheExtensions.Get yöntemini kullanarak bu bilgileri bir saat boyunca kolayca önbelleğe alabilir ve bu bilgilere her ihtiyaç duyulduğunda DB çağrılarından kaçınabiliriz.

public UserProfile GetProfile(int userID)
{
	return localCache.Get<UserProfile>(
		cacheKey: "UserProfile:" + userID,
		expiration: TimeSpan.FromHours(1),
		loader: delegate {
			using (var connection = new SqlConnection("..."))
			{
				// load profile by userID from DB
			}
		}
	);
}

WEB Grupları ve Önbelleğe Alma

Bir sosyal ağ sitemizin ve milyonlarca kullanıcı profilimizin olduğunu düşünelim. Bazı ünlü kullanıcıların profil sayfaları dakikada yüzlerce veya binlerce ziyaret alıyor olabilir.

Bir kullanıcı profili oluşturmak için birden fazla SQL sorgusuna ihtiyacımız var (arkadaşlar, albüm adları ve resim sayıları, profil bilgileri, son durum vb.).

Bir kullanıcı profilini güncellemediği sürece sayfasında gösterilen bilgiler neredeyse statik olacaktır. Böylece profil sayfalarının anlık görüntüsü 5 dakika veya 1 saat vb. süreyle önbelleğe alınabilir.

Ancak bu yeterli olmayabilir. Yüz milyonlarca profil ve kullanıcıdan bahsediyoruz. Kullanıcılar yalnızca bazı profil sayfalarına bakmaktan çok daha fazlasını yapıyor olacaktır. Dünyanın çeşitli coğrafi konumlarına dağıtılmış birden fazla sunucuya (WEB Çiftliği) ihtiyacımız olacaktır.

Belirli bir zamanda, tüm bu sunucular çok önemli bir kişinin (VIP) profilini yerel önbellekte önbelleğe almış olabilir. VIP, profilinde bir değişiklik yaptığında, tüm bu sunucuların yerel önbelleğe alınmış profillerini yenilemeleri gerekir ve bu, birkaç saniye içinde gerçekleşir. Artık kullanıcı başına yük yerine sunucu başına yük ile ilgili bir sorunumuz var.

Bu sunuculardan biri VIP profilini SQL veritabanından yükleyip önbelleğe aldığında, diğer sunucular veritabanına çarpmadan aynı bilgileri kullanabilir. Ancak her sunucu, önbelleğe alınmış bilgileri kendi yerel belleğinde sakladığından, bu bilgilere diğer sunucuların erişmesi önemsiz değildir.

Tüm sunucuların erişebileceği ortak bir hafızamız olsaydı:

Bilgi anahtarıDeğer
Profil:ÇokFamousOne(VeryFamousOne için önbelleğe alınmış bilgiler)
Profil:SomeAnother...
......
......
Profil: JohnDoe...

Bu belleğe dağıtılmış önbellek adını verelim. Eğer tüm sunucular DB'yi denemeden önce bu ortak belleğe bakarsa, sunucu başına yükleme sorununu önlemiş oluruz.

Memcached ve Redis dahil olmak üzere dağıtılmış önbellek sistemlerinin birçok çeşidini bulabilirsiniz. Bunlara veritabanları da denir NoSQLBunları basitçe uzak bir sözlük olarak düşünebilirsiniz. Anahtar/değer çiftlerini hafızalarında saklarlar ve bunlara mümkün olduğunca hızlı erişmenizi sağlarlar.

Uyarı! Dağıtılmış önbellek, doğru kullanıldığında tıpkı yerel önbellek gibi uygulamanızın performansını artırabilir. Aksi takdirde, ağ aktarımı ve serileştirme maliyeti söz konusu olduğundan yerel önbelleğe göre daha kötü bir etkiye sahip olabilir. Dolayısıyla "eğer bazı şeyleri dağıtılmış önbellekte tutarsak sitemiz daha hızlı çalışır" bir efsanedir.

Önbelleğe alınan veriler çok fazla olduğunda, bir bilgisayarın belleği tüm anahtar/değer çiftlerini depolamaya yetmeyebilir. Bu durumda Redis gibi sunucular verileri kümeleyerek dağıtır. Bu, tuşların ilk harfiyle yapılabilir. Bir sunucu A ile başlayan, diğerleri B ile başlayan çiftleri tutabilir, vb. Bu amaç için genellikle anahtarların karma değerini kullanırlar.

DistributedCacheExtensions Sınıfı

DistributedCacheExtensions'a benzer şekilde MemoryCacheExtensionsve dışındaki türler için JSON serileştirmesiyle çalışmayı ve kullanmayı kolaylaştırmak için çeşitli uzantı yöntemleri içerir .IDistributedCacheStringbyte[]

SetAutoJson genel yöntemi, önbellekteki bir öğenin a stringveya byte[]değerleriyle ayarlanmasına olanak tanır veya JSON, onu başka herhangi bir tür için serileştirir.

distributedCache.SetAutoJson("SomeByteArray", new byte[] { 1, 3, 5, });
distributedCache.SetAutoJson("SomeString", "SomeString");

distributedCache.SetAutoJson("SomeKey", new MyObject { ... });
distributedCache.SetAutoJson("AnotherKey", new MyObject { ... }, TimeSpan.FromMinutes(5));

GetAutoJson genel yöntemi , yaptığının tam tersini yapar SetAutoJsonJSON, stringveya dışında herhangi bir türü seri durumdan çıkarır byte[].

var byteArray = distributedCache.GetAutoJson<byte[]>("SomeKey");
var str = distributedCache.GetAutoJson<string>("SomeKey");
var myObj = distributedCache.GetAutoJson<MyObject>("SomeKey");

Serenity herhangi bir özel uygulama sağlamaz IDistributedCacheSerene ve StartSharp DistributedMemoryCachevarsayılan olarak bunu kullanır.

Varsayılan uygulamalar, dosyadaki çağrı yoluyla kaydedilir IMemoryCacheve bu çağrı, CoreServiceCollectionExtensions'ın uzantı yöntemini çağırır.IDistributedCacheStartup.csAddServiceHandlersStartup.csAddCaching

Redis veya SQL Server gibi diğer dağıtılmış önbellek sunucularını etkinleştirme talimatları için .NET Önbelleğe Alma belgesini görebilirsiniz . Dosyadaki veya çağrılarından IDistributedCacheönce özel uygulamayı kaydettiğinizden emin olun .AddServiceHandlersAddCachingStartup.cs

İki Seviyeli Önbelleğe Alma

Yerel (bellek içi) önbelleğe almayı kullandığınızda, bir sunucu bazı bilgileri önbelleğe alabilir ve mümkün olduğu kadar hızlı bir şekilde alabilir, ancak diğer sunucular önbelleğe alınan verilere erişemediğinden, aynı bilgileri veritabanından sorgulamak zorundadırlar.

Bazı serileştirme/seri durumdan çıkarma ve ağ gecikmesi ek yükü nedeniyle diğer sunucuların önbelleğe alınmış verilere erişmesine izin vermek için dağıtılmış önbelleğe almayı tercih ederseniz, bazı durumlarda performansı düşürebilir.

Önbelleğe almayla ilgili ele alınması gereken başka bir sorun daha vardır: önbelleğin geçersiz kılınması:

Bilgisayar Bilimlerinde yalnızca iki zor şey vardır: önbelleği geçersiz kılmak ve şeyleri adlandırmak.

--Phil Karlton

Bazı bilgileri önbelleğe aldığınızda, kaynak veriler değiştiğinde önbelleğe alınan bilgilerin geçersiz kılındığından (yeniden oluşturulduğundan veya önbellekten kaldırıldığından) emin olmalısınız.

Yerel Önbelleği ve Dağıtılmış Önbelleği Senkronize Etme

Basit bir algoritmayı takip ederek her iki dünyanın en iyilerinden faydalanabiliriz:

  1. Yerel önbellekte bir anahtar olup olmadığını kontrol edin.
  2. Anahtar yerel önbellekte mevcutsa değerini döndürün.
  3. Anahtar yerel önbellekte yoksa dağıtılmış önbelleği deneyin.
  4. Anahtar dağıtılmış önbellekte mevcutsa değerini döndürün ve onu da yerel önbelleğe ekleyin.
  5. Anahtar dağıtılmış önbellekte yoksa, onu veritabanından üretin ve hem yerel önbelleğe hem de dağıtılmış önbelleğe ekleyin. Üretilen değeri döndürün.

Bu şekilde, bir sunucu bazı bilgileri yerel önbellekte önbelleğe aldığında, bunu aynı zamanda dağıtılmış önbellekte de önbelleğe alır, ancak bu sefer diğer sunucular, eğer belleklerinde yerel bir kopya yoksa, dağıtılmış önbellekteki bilgileri yeniden kullanabilirler.

Tüm sunucuların yerel bir kopyası olduğunda, hiçbirinin dağıtılmış önbelleğe tekrar erişmesine gerek kalmayacak, böylece serileştirme ve gecikme yükü ortadan kalkacak.

Yerel Kopyaları Doğrulama

Her şey yolunda görünüyor. Ancak şimdi önbellek geçersiz kılma sorunumuz var. Sunuculardan birinde önbelleğe alınan veriler değiştirilirse ne olur? Yerel olarak önbelleğe alınmış kopyalarını geçersiz kılabilmeleri için onları bu değişiklikten nasıl haberdar edeceğiz ?

Dağıtılmış önbellekteki değeri değiştirirdik, ancak artık dağıtılmış önbelleği kontrol etmedikleri için (son algoritmada 2. adımdaki kısayol) fark edilmezler.

Bu soruna bir çözüm, yerel kopyaları belirli bir süre (örneğin 5 saniye) tutmak olabilir. Böylece bir sunucu önbelleğe alınan veriyi değiştirdiğinde diğer sunucular çoğunlukla 5 saniye boyunca güncel olmayan bilgileri kullanır.

Bu yöntem, önbelleğe alınmış aynı bilgilerin tekrar tekrar kullanılmasını gerektiren toplu işlemlerde yardımcı olacaktır. Ancak dağıtılmış önbellekte hiçbir şey değişmese bile, her 5 saniyede bir dağıtılmış önbellekten yerel önbelleğe bir kopya almamız gerekir. Önbelleğe alınan veriler büyükse, bu durum ağ bant genişliği kullanımını ve seri durumdan çıkarma maliyetini artıracaktır.

Dağıtılmış önbellekteki verilerin yerel kopyadan farklı olup olmadığını bilmenin bir yoluna ihtiyacımız var. Bunun hayal edebileceğim birkaç yolu var:

  • Karmayı verilerle birlikte yerel ve dağıtılmış önbellekte depolayın (hafif karma hesaplama maliyeti)
  • Artan sürüm numarasına sahip verileri depolayın (iki sunucunun aynı sürüm numaralarını oluşturmadığından nasıl emin olunur?)
  • Verilerin dağıtılmış önbellekte en son ayarlandığı zamanı depolayın (zaman senkronizasyonu sorunları)
  • Verilerin yanında rastgele bir sayı (nesil) saklayın

Serenity, sürüm olarak nesil sayılarını (rastgele int) kullanır.

Yani dağıtılmış önbellekte bir değer sakladığımızda, diyelim ki SomeCachedKey , ayrıca SomeCachedKey$GENERATION$ anahtarıyla rastgele bir sayı da saklıyoruz .

Şimdi önceki algoritmamız şu oluyor:

  1. Yerel önbellekte bir anahtar olup olmadığını kontrol edin.
  2. Anahtar yerel önbellekte mevcutsa
    • Kendi neslini dağıtılmış önbellekteki nesille karşılaştırın
    • Eşitlerse yerel olarak önbelleğe alınan değeri döndürün
    • Eşleşmiyorlarsa 4'e devam edin
  3. Anahtar yerel önbellekte yoksa dağıtılmış önbelleği deneyin.
  4. Anahtar dağıtılmış önbellekte mevcutsa, değerini döndürün ve onu da oluşturmanın yanı sıra yerel önbelleğe ekleyin.
  5. Anahtar dağıtılmış önbellekte mevcut değilse, onu veritabanından oluşturun ve rastgele bir nesille hem yerel önbelleğe hem de dağıtılmış önbelleğe ekleyin. Üretilen değeri döndürün.

Önbelleğe Alınmış Birden Çok Öğeyi Tek Çekimde Doğrulama

Bazı tablolardan üretilen verileri önbelleğe almış olabilirsiniz. Bu tablo için dağıtılmış önbellekte birden fazla anahtar bulunabilir.

Diyelim ki bir profil tablonuz var ve profil öğelerinizi Kullanıcı Kimliği değerlerine göre önbelleğe aldınız.

Bir kullanıcının profil bilgileri değiştiğinde, önbelleğe alınmış profilini önbellekten kaldırmayı deneyebilirsiniz. Peki ya bilmediğiniz başka bir sunucu veya uygulama aynı kullanıcı profili verilerinden oluşturulan bazı bilgileri önbelleğe aldıysa? Bazı kullanıcı kimliklerine bağlı olarak dağıtılmış önbellekte hangi önbelleğe alınmış bilgi anahtarlarının bulunduğunu bilmiyor olabilirsiniz.

Çoğu dağıtılmış önbellek uygulaması, bir dizeyle başlayan tüm anahtarları bulmanın bir yolunu sağlamaz veya hesaplama açısından yoğundur (sözlük tabanlı oldukları için).

Dolayısıyla, bir dizi veriye bağlı olarak tüm öğelerin süresinin dolmasını istediğinizde bu mümkün olmayabilir.

Serenity, öğeleri önbelleğe alırken, grubun bağlı olduğu veriler değiştiğinde bunların süresinin dolmasını sağlayacak bir grup anahtarı belirlemenize olanak tanır.

Diyelim ki bir uygulama ID 17'nin profil verilerine sahip bir kullanıcıdan CachedItem17 üretti ve bu ID'yi grup anahtarı olarak kullanıyoruz (Group17_Generation):

AnahtarDeğer
Önbelleğe Alınmış Öğe17cxyzyxzcasd
Önbelleğe AlınmışItem17_Generation13579
Grup17_Generation13579

Burada grubun rastgele üretimi (versiyonu) 13579'dur. Önbelleğe alınmış verilerle (CachedItem17) birlikte, bu verileri ürettiğimizde grup üretimi ne olursa olsun (CachedItem17_Generation) depoladık.

Başka bir sunucunun, Kullanıcı 17'nin verilerinden AnotherItem17'yi önbelleğe aldığını varsayalım:

AnahtarDeğer
Önbelleğe Alınmış Öğe17cxyzyxzcasd
Önbelleğe AlınmışItem17_Generation13579
Başka Bir Öğe17uwsdasdas
AnotherItem17_Generation13579
Grup17_Generation13579

Burada Group17_Generation'ı yeniden kullandık, çünkü dağıtılmış önbellekte zaten bir grup sürüm numarası vardı, aksi takdirde yeni bir tane oluşturmamız gerekecekti.

Artık önbellekteki her iki öğe de (CachedItem17 ve AnotherItem17) geçerlidir çünkü sürüm numaraları grup sürümüyle eşleşir.

Birisi Kullanıcı 17'nin verilerini değiştirmişse ve biz onunla ilgili önbelleğe alınmış tüm öğelerin süresinin dolmasını istiyorsak, yalnızca grup oluşturmayı değiştirmemiz gerekir:

AnahtarDeğer
Önbelleğe Alınmış Öğe17cxyzyxzcasd
Önbelleğe AlınmışItem17_Generation13579
Başka Bir Öğe17uwsdasdas
AnotherItem17_Generation13579
Grup17_Generation54237

Artık önbelleğe alınan tüm öğelerin süresi doldu. Önbellekte var olmalarına rağmen nesillerinin grup nesliyle eşleşmediğini, dolayısıyla geçerli sayılmadığını görüyoruz.

Kullandığımız grup anahtarları genellikle verinin üretildiği tablonun adıdır.

ITwoLevelCache Arayüzü

Fark ettiğiniz gibi hafızayı ve dağıtılmış önbellekleri birlikte kullanmamız gerekiyor.

ITwoLevelCache arayüzü , bir bellek önbelleği ile dağıtılmış önbelleğin birleşimidir:

public interface ITwoLevelCache
{
    IMemoryCache Memory { get; }
    IDistributedCache Distributed { get; }
}

TwoLevelCacheExtensions Sınıfı

Statik TwoLevelCacheExtensionsITwoLevelCache sınıfı , örnekler üzerinde çalışan ve şu ana kadar konuştuklarımızı (örn. aralarında senkronizasyon ve önbellek geçersiz kılma) uygulayan uzantı yöntemlerini içerir .

TwoLevelCacheExtenison.Get Yöntem

  • Yerel önbellekten bir değer okumaya çalışır. Orada bulunamazsa (veya süresi dolmuş bir sürümü varsa), dağıtılmış önbelleği dener.

  • Her ikisi de belirtilen anahtarı içermiyorsa, bir yükleyici işlevini çağırarak değer üretir ve değeri belirli bir süre sonu için yerel ve dağıtılmış önbelleğe ekler.

  • Get yönteminin iki aşırı yüklemesi vardır. Biri yerel ve dağıtılmış önbellekler için ayrı ayrı sona erme süresini alır, diğeri ise her ikisi için de yalnızca bir süre sonu parametresine sahiptir.

  • Bir grup anahtarı kullanılarak, her iki önbellek türündeki bu grubun üyesi olan tüm öğelerin süresi bir defada dolabilir (bu, nesile dayalı bir sona ermedir, zamana değil).

Gruba ait bir öğe her istendiğinde grup oluşturmanın kontrol edilmesini önlemek için grup oluşturmanın kendisi de yerel önbellekte önbelleğe alınır. Bu nedenle, bir nesil numarası değiştiğinde, yerel önbelleğe alınan öğelerin süresi 5 saniye sonra dolabilir.

Bu, bir web grubu kurulumunda bu stratejiyi kullanırsanız, bir sunucuda değişiklik meydana geldiğinde diğer sunucuların eski yerel önbelleğe alınmış verileri 5 saniye daha kullanmaya devam edebileceği anlamına gelir.

Bu, yapılandırmanız için bir sorunsa TwoLevelCache'e bağlı olmak yerine doğrudan DistributedCache yöntemlerini kullanmalısınız.

TwoLevelCache.Get Akış Diyagramı

CachedProfile GetCachedProfile(int userID)
{
    twoLevelCache.Get("CachedProfile:" + userID, TimeSpan.FromDays(1), "SomeGroupKey",
        () =>
        {
            using (var connection = new SqlConnection("..."))
            {
                connection.Open();
                return LoadProfileFromDB(connection, userID);
            }
        });
}

CachedProfile LoadProfileFromDB(IDbConnection connection, int userID)
{
    // ...
}

TwoLevelCache.GetLocalStoreOnly Yöntem

Öğeleri dağıtılmış önbellek yerine yalnızca yerel önbellekte depolamak istiyorsanız GetLocalStoreOnly yararlı olabilir.

Bir sunucu tarafından önbelleğe alınan veriler diğerleri için yararlı olmadığında (sunucudan sunucuya değişiklikler), serileştirme/seri durumdan çıkarma çok büyük veya yavaş olduğunda, bu tür verileri dağıtılmış önbellekte depolamak anlamlı değildir.

Peki bu durumda neden doğrudan LocalCache kullanmıyorsunuz?

Bir grup anahtarı belirlemek ve o grubun kaynak verileri değiştiğinde (dağıtılmış önbellekte saklanıyorlarmış gibi) yerel önbelleğe alınmış öğelerin geçerliliğini kolayca sona erdirmek istiyorsanız bunu yapabilirsiniz, ancak yapamazsınız.

TwoLevelCache.ExpireGroupItems Yöntem

Bu yöntem, bir grup anahtarının üyesi olan tüm öğelerin süresinin dolmasına olanak tanır. Grup anahtarını yerel önbellekten ve dağıtılmış önbellekten kaldırır, böylece bir sonraki sorgulandığında başka bir sürüm oluşturulur.

twoLevelCache.ExpireGroupItems("SomeGroupKey");

Bunu verileri değiştiren yöntemlerden çağırmalısınız.

Oluşturma, Güncelleme, Silme ve Silmeyi Geri Alma işleyicileri bunu grup anahtarı olarak ConnectionKey.TableName ile otomatik olarak yapar.

TwoLevelCache.Remove Yöntem

Bir öğeyi ve sürümünü yerel ve dağıtılmış önbellekten kaldırır.

Bu blogdaki popüler yayınlar

Code generetor ile oluşturulan dosyaların açıklamaları

  1. Sunum (Presentation/UI) Katmanı (Kullanıcı arayüzü - HTML, TypeScript, Dialog, Grid) 🔹 XYZPage.ts 📌 Ne İşe Yarar? Kullanıcı arayüzünün TypeScript tarafındaki tanımıdır. Serenity'nin Dialog ve Grid bileşenlerini içeren bir TypeScript sınıfıdır. 📌 Çok Katmanlı Mimarideki Yeri: Sunum Katmanı (Presentation Layer) Kullanıcıdan veri almak ve göstermek için kullanılır. 🔹 XYZGrid.ts 📌 Ne İşe Yarar? Tablo (Grid) yapısını oluşturur ve verileri listeler. Filtreleme, sıralama ve sayfalama işlemleri için kullanılır. columnsKey ile hangi kolonların gösterileceğini belirler. 📌 Çok Katmanlı Mimarideki Yeri: Sunum Katmanı (Presentation Layer) Kullanıcının verileri listelediği ve etkileşimde bulunduğu yerdir. 🔹 XYZDialog.ts 📌 Ne İşe Yarar? CRUD (Create, Read, Update, Delete) işlemlerini yöneten pencere (modal) bileşeni Kullanıcı form aracılığıyla veri ekler, günceller veya siler. XYZForm.cs ile birlikte çalışır. 📌 Çok Katmanlı Mimarideki Yeri: Sunum Katmanı (Presentation Layer) Kull...

Serenity Web Nedir?

   Serenity  , açık kaynak teknolojileri üzerine kurulu bir ASP.NET Core/TypeScript uygulama platformudur. Standart kodlardan kaçınarak, tekrarlanan görevlere harcanan zamanı azaltarak ve en iyi yazılım tasarımı uygulamalarını uygulayarak bakım maliyetlerini düşürürken geliştirmeyi kolaylaştırmayı amaçlamaktadır. Serene  , Serenity platformunu temel alan ücretsiz, açık kaynaklı başlangıç ​​uygulama şablonumuzdur.  Bu dokümantasyon aracılığıyla eğitimimiz ve diğer örnekler için esas olarak Serene'yi kullanacağız. StartSharp  , ücretli müşterilerimize sunduğumuz premium uygulama şablonudur.  Daha gösterişli bir temaya ve bazı ekstra özelliklere  ek olarak Serene'deki her şeyi içerir  .  İkisi de Serenity platformunu temel alıyor. Adında Ne Var Serenity'nin sözlük anlamları  barış  ,  rahatlık  ve  sakinliktir  . Serenity ile bunu başarmaya çalışıyoruz.  Umarız yükledikten ve kullandıktan sonra siz de bu ş...