Ana içeriğe atla

Lookup Scripts

 Bilgisayar bilimlerinde arama tablosu (LUT), çalışma zamanı hesaplamasını daha basit bir dizi indeksleme işlemiyle değiştiren bir dizidir.

Serenity'deki aramalar, varsayılan olarak sütun gibi doğal bir sıraya göre sıralanan, bir Nameaçılır menüde görüntülenmeye uygun ve ayrıca özelliği aracılığıyla belirli bir öğeye hızlı bir şekilde erişme olanağı sağlayan bir bellek içi tablo gibi bir nesne dizisidir IDBazı insanlar onları arayabilir "reference tables".

Q.Lookup Sınıfı

a'nın ne olduğunu açıklamadan önce öncelikle paketteki TypeScript sınıfına Lookup Scriptbakmalıyız :Lookup@serenity-is/corelib

class Lookup<TItem> {
    constructor(options: LookupOptions<TItem>, items?: TItem[]);
    items: TItem[];
    itemById: { [key: string]: TItem; };
    idField: string;
    textField: string;
    //...
}

interface LookupOptions<TItem> {
    idField?: string;
    textField?: string;
    //..
}

"idField"(örneğin ID özelliği), "textField"(örneğin Name özelliği) ve bir dizi nesne hakkında bilgi içeren bir seçenekler nesnesini kabul eden bir kurucuya sahiptir .

Bir dizi dil için bir arama nesnesi oluşturalım.

var languages = new Q.Lookup({
    idField: 'Code',
    textField: 'Name',
}, [
    {
        Code: 'en-US',
        Name: 'English (US)'
    },
    {
        Code: 'de-DE',
        Name: 'German'
    },
    {
        Code: 'fr-FR',
        Name: 'French'
    }
    //...
]);

Yukarıdaki kodu https://demo.serenity.is adresinde bir geliştirici konsolunda deneyebilirsiniz .

Geliştirici Konsolunda Arama

Yukarıda görüldüğü gibi Lookup örneği, options aracılığıyla yapıcıya ilettiğimiz özellik adlarını döndüren alan ve ikinci argüman aracılığıyla aktarılan diziyi döndüren alan idFieldiçeren bir nesnedir.textFielditems

itemByIdÖğelerin değerlerine göre bir sözlüğü olan adında ek bir özellik de vardır "idField":

Developer Console'da itemById'i ara

CodeBu bir sözlük olduğu için öğelerin ID ( ) özelliklerindeki değerlerin benzersiz olması beklendiği açık olmalıdır .

Bu arama yoluyla sıradan bir öğenin seçeneklerini doldursaydık SELECT, şöyle görünürdü:

<select>
    <option value="en-US">English (US)</option>
    <option value="de-DE">German</option>
    <option value="fr-FR">French</option>
</select>

Sınıf Lookupbaşlangıçta açılır öğeleri doldurmak için tasarlandı; bir öğeye kimliğine göre erişme, öğelerin Kimlik ve Metin dışındaki özelliklerini okuyabilme ve aramayı referans alan açılır menülerin güncellenebilmesi için kaynak veriler değiştiğinde bildirimleri artırmanın bir yolunu sağlama gibi bazı ek özellikler sağlarken onların seçenekleri.

Q.ScriptVeri Kaydı

Q.ScriptDataaramalar gibi adlandırılmış veri nesneleri için merkezi bir kayıt defteridir. IDynamicScriptManagerBunu istemci tarafında çalışan bir versiyon olarak düşünebilirsiniz .

Dil aramamızı özel bir anahtarla kaydedelim:

ScriptData Arama Kaydı

Q.ScriptData.setLookup.MyLanguagesyöntem, yukarıdaki örnekte olduğu gibi iletilen belirtilen anahtara sahip bir nesneyi kaydeder .

"Lookup."kayıt defterindeki arama nesneleri için standart önektir ScriptData.

lookup keyKayıtlı arama nesnelerine daha sonra örneğin örneğimizde MyLanguagessonraki kısım aracılığıyla erişilebilir Lookup..

Var olmayan bir aramayı almaya çalışırsak ne olacağını görelim:

Konsolda var olmayan arama

Yukarıdaki ekran görüntüsünde görüldüğü gibi, Q.getLookupişlev, URL'ye yapılan bir çağrı yoluyla aramayı sunucudan yüklemeye çalıştı ~/DynJS.axd/Lookup.MyFriends.jsDinamik komut dosyası yöneticisinin isimle kayıtlı bir komut dosyası olmadığından , örneğin "Lookup.MyFriends"bir yanıt verdi .404Not Found

LookupScriptArdından, istemci tarafındaki hata işleyicimiz devreye girdi ve sizi olası bir yazım hatası veya sunucu tarafındaki varlık nesnelerinizden birine bir nitelik eklemeyi unutma konusunda bilgilendirmeye çalıştı .

Uyarı: Q.getLookup Kullanılmıyor

Yöntemin eski olduğunu unutmamalı Q.getLookupve onun yerine onu tercih etmelisiniz Q.getLookupAsyncBurada sadece numune almak için kullanıyoruz.

Birincisi, ana tarayıcı kullanıcı arayüzü iş parçacığını bloke ederek senkronize bir XHR isteği gerçekleştirirken, ikincisi eşzamansız olarak çalışır ve bir söz verir.

Bunu kullanırsanız Q.getLookup, istek devam ederken tarayıcı sekmesini engelleyeceksiniz ve tarayıcı konsolunda aşağıdakine benzer bir uyarı alacaksınız:

Senkronizasyon isteği kullanımdan kaldırıldı

Q.getLookupUyumluluk nedeniyle, yöntemi veya onu çağıran değişkenleri kaldıramayız MyRow.getLookup()ancak mümkün olduğunca bunları kullanmaktan kaçınmalısınız.

Arama Komut Dosyasının İçeriği

DynJS.axdGerçek bir arama komut dosyası için neyin döndürüldüğünü görelim :

Yönetici dili araması

Yukarıdaki ekran görüntüsü, özelliğin kullanılmasında tanımlanan şey için döndürülen şeyi "Lookup.Administration.Language"gösterir .LanguageRow[LookupScript]

Bunun, özel dil aramamız için tanımladığımıza çok benzediğini, Q.ScriptData.setbazı seçenekler ve bir dizi dil öğesi içeren bir ifade içerdiğini fark edeceksiniz.

Çoğu dinamik komut dosyası, Q.ScriptData.setkomut dosyası türüne ve adına dayalı bir anahtara sahip bu kadar basit bir blok içerir. Geriye kalan içerik yalnızca JSON serileştirilmiş bir nesne dizisidir.

Veri kısmını sadece JSON formatında almak istiyorsak, örneğin parça olmadan , şunun yerine uç noktayı Q.ScriptData.setkullanabiliriz :DynamicDataDynJS

Dinamik Veriler - Diller

Yukarıdaki oldukça biçimlendirilmiş ekran, kullandığım bir uzantıdan kaynaklanıyor

LookupScript Özelliği

LookupScript özelliği, sunucu tarafı kodunda , uç nokta aracılığıyla istemci tarafından erişilebilen bir arama komut dosyasını tanımlamak için kullanılır DynJS.

Dinamik bir komut dosyası şu şekilde Lookup.Administration.Roletanımlanır RoleRow.cs:

[ConnectionKey("Default"), Module("Administration"), TableName("Roles")]
[ReadPermission(PermissionKeys.Security)]
[LookupScript]
public sealed class RoleRow : Row<RoleRow.RowFields>, IIdRow, INameRow
{
    [DisplayName("Role Id"), IdProperty]
    public int? RoleId
    {
        get => fields.RoleId[this];
        set => fields.RoleId[this] = value;
    }

    [DisplayName("Role Name"), NameProperty]
    public string RoleName
    {
        get => fields.RoleName[this];
        set => fields.RoleName[this] = value;
    }

    public string RoleKey
    {
        get => fields.RoleKey[this];
        set => fields.RoleKey[this] = value;
    }
}

Arama Anahtarı

Oluşturulan arama komut dosyasının adı önekle başlar Lookup.ve ardından arama anahtarı gelir.

Arama anahtarı özniteliğe manuel olarak aktarılabilir LookupScript:

[LookupScript("MyModule.MyLookup")]

Veya yukarıdaki örnekte olduğu gibi , geleneksel olarak, hangi is'den ve hangi is eki olmadan varlık türü adından Rolebelirlenebilir ve arama anahtarı olarak " " elde edilir .ModuleAdministrationRowRoleAdministration.Role

Hedef tipin bir niteliği yoksa Modulemodül adı ad alanından belirlenecektir.

AramaDahil Et Özelliği

Arama komut dosyasındaki öğeler yalnızca varlığın Idve özelliklerini içerecektir; örneğin, ve Namenitelikleriyle işaretlenenler .IdPropertyNameProperty

Bazı ek özellikler eklemek istiyorsanız LookupIncludebunların üstüne bir özellik eklemeniz gerekir:

[LookupInclude]
public string RoleKey
{
    get => fields.RoleKey[this];
    set => fields.RoleKey[this] = value;
}

Arama İzni

Varsayılan olarak, arama komut can be accessed by anyonedosyası anonymous users.

ReadPermissionAncak öznitelik , yukarıdaki RoleRow örneğinde olduğu gibi bir öznitelik ile türe yerleştirilirse , ReadPermissionarama komut dosyasını kaydederken otomatik olarak değerini alır:

[ReadPermission(PermissionKeys.Security)]
[LookupScript]
public sealed class RoleRow

Dolayısıyla yukarıdaki arama komut dosyası için, bu arama komut dosyasına erişmeye çalışan herhangi bir kullanıcının " " iznine sahip olması gerekir Administration:Security.

İzni manuel olarak belirtmek isterseniz bunu aşağıdaki gibi yapabilirsiniz:

[LookupScript(Permission = "SomePermission")]

Bu durumda ReadPermissionöznitelik göz ardı edilecek ve "SomePermission"onun yerine kontrol edilecektir.

Herhangi birinin arama komut dosyasına erişmesine izin vermek için *izin olarak " " belirtin, yalnızca kimliği doğrulanmış kullanıcılara izin vermek için bunu " " olarak belirtebilirsiniz ?.

UYARI!

İzinler yalnızca DynJS/DynamicData uç noktasından arama komut dosyası istendiğinde kontrol edilir. Komut dosyası zaten istemci tarafında önbelleğe alınmışsa izin kontrolünü gerçekleştirmenin bir yolu yoktur.

Dolayısıyla, yönetici iznine sahip bir kullanıcının sitenizde oturum açtığı ve bazı aramalara eriştiği, ardından oturumu kapattığı ve bir başkasının aynı tarayıcıyı anonim olarak veya oturum açarak kullandığı bir senaryoda, eğer tarayıcı arama komut dosyasını önbelleğe aldıysa, önbelleğe alınmış sürüm, uç noktalara erişmeye gerek kalmadan döndürülecektir DynJS/DynamicData.

Bu sizin için bir güvenlik sorunuysa, aramalarda herhangi bir hassas veri saklamayın veya aramaların hiçbir zaman istemci tarafında önbelleğe alınmaması için ilgili HTTP başlıklarını ayarlamayın.

Özel Aramalar

Dil araması StartSharp/Sereneaşağıdaki gibi özel bir satır araması olarak tanımlanır:

[LookupScript]
public sealed class LanguageLookup : RowLookupScript<LanguageRow>
{
   public LanguageLookup(ISqlConnections sqlConnections)
       : base(sqlConnections)
   {
       IdField = LanguageRow.Fields.LanguageId.PropertyName;
       Permission = "*";
   }

   protected override void PrepareQuery(SqlQuery query)
   {
       base.PrepareQuery(query);

       query.Select(LanguageRow.Fields.LanguageId);
   }
}

Bu örnekte LanguageLookup, RowLookupScriptaynı zamanda otomatik olarak oluşturulan satır arama komut dosyalarının da sınıfı olan temel sınıftan türetilmiştir.

Sınıf yapıcısında, IdField'ı manüel olarak özelliğe ayarladık LanguageId; LanguageRow'un kimlik alanı ise özelliktir Id:

public sealed class LanguageRow
{
    [DisplayName("Id"), Identity, IdProperty]
    public Int32? Id

    [DisplayName("Language Id")]
    public String LanguageId

    [DisplayName("Language Name"), NameProperty]
    public String LanguageName

Bunu özel arama komut dosyası olarak tanımlamamızın nedenlerinden biri de budur; örneğin, varlığın IdField" " dışında bir şeye ayarlamak.IdProperty

Ayrıca yöntemde , özelliğin üzerine bir özellik eklemeden PrepareQuerymanuel olarak dahil ediyoruz .LanguageIdLookupInclude

Aksi takdirde, temel yöntem yalnızca seçme ve özellikleri PrepareQuerykullanır .IdLanguageName

*Dil seçiminin oturum açmadan yapılabilmesi gerektiğinden, anonim kullanıcıların bu arama komut dosyasına erişmesine izin vermek için yapıcıda izni olarak ayarladık .

Arama Yükleme İşlemi

Önceki örneğimizde gördüğümüz gibi, aramalar ilk kez or çağrıları DynJSaracılığıyla istendiğinde uç noktadan otomatik olarak yüklenecektir .Q.getLookupQ.getLookupAsync

Geçerli sayfaya bir arama yüklendiğinde, bellekte (örneğin, Q.ScriptData sözlüğünde) önbelleğe alınacaktır ve bir dahaki sefere uç noktayı çağırmadan veya önbelleğe alınmış sürüm aracılığıyla Q.getLookupistendiğinde .Q.getLookupAsyncDynJS

Bazı durumlarda, komut dosyası bellek önbelleğinde olmasa bile, tarayıcı dinamik komut dosyasını zaten önbelleğe almışsa, uç noktayı çağırmadan onu döndürebilir DynJS.

Q.getLookupAsync vb. çağırdığınızda ne olacağının adımlarını listelemeye çalışalım.

  • Komut dosyası zaten Javascript bellek önbelleğinde, örneğin ScriptData sözlüğünde bulunuyorsa, onu doğrudan döndürün
  • Komut dosyasının sunucu tarafındaki kayıtlı komut dosyaları listesinde olup olmadığını kontrol edin. RegisteredScriptsBu liste sayfa yüklemesindeki özel sözlük aracılığıyla sağlanır . Komut dosyası adlarını ve bunların karmalarını içerir. Değilse, aracılığıyla rastgele bir karma oluşturun new Date().getTime().toString().
  • GETŞuraya bir istek gönder:~/DynJS.axd/{scriptname}.js?v={hash}
  • Tarayıcının önbelleğinde zaten bu URL için bir eşleşme varsa, sunucuyu çağırmadan bunu döndürebilir.
  • Tarayıcının önbelleğinde yoksa, komut dosyası daha önce yüklenmediğinden veya karma değiştiğinden dolayı GETsunucuya bir istek gerçekleştirir.
  • DynamicScriptMiddlewareisteği yakalar ve IDynamicScriptManagerböyle bir betiğin {scriptname}Değilse, bir ile yanıt verir 404.
  • DynamicScriptManager, geçerli kullanıcının bu komut dosyası için gerekli izinlere sahip olup olmadığını kontrol eder. Aksi takdirde erişim ihlali ortaya çıkar.
  • DynamicScriptManager, bellekte bu betiğin önbelleğe alınmış bir sürümünün bulunup bulunmadığını ve hala geçerli olup olmadığını, örneğin süresinin dolmamış olup olmadığını kontrol eder. Eğer öyleyse, önbelleğe alınmış sürümü döndürür.
  • DynamicScriptManager'ın önbelleğe alınmış bir sürümü yoksa dinamik betiğin GetScriptyöntemini çağırır. Arama komut dosyaları için bu, bir veritabanı sorgusu gerçekleştirir.
  • Dinamik komut dosyası önbelleğe alınabilir olarak işaretlendiyse, örneğin Sona Erme değeri negatif değilse, DynamicScriptManager oluşturulan komut dosyasını GZIP/Brotli sıkıştırılmış sürümleriyle birlikte önbelleğe alır.
  • Tarayıcıya bağlı olarak Başlıkları kabul et, DynamicScriptMiddlewareham komut dosyasını veya sıkıştırılmış sürümlerden birini GZIP/Brotlitarayıcıya gönderir. Modern tarayıcılar için neredeyse her zaman sıkıştırılmıştır Brotli.
  • ScriptData betiği alır ve onu tarayıcının genel bağlamında çalıştırır evalve verileri bir dahaki sefere dahili bellek içi sözlüğünde önbelleğe alır.
  • ScriptData istenen verileri döndürür.

Aramalar Yeniden Yükleniyor

Önceki başlıkta da belirttiğimiz gibi, aramalar her iki istemci tarafında da önbelleğe alınır; ve sunucu tarafında ve tarayıcının kendisi dahil çeşitli katmanlarda.

İstemci tarafında bir arama komut dosyasının yeniden yüklenmesini zorlamak istiyorsanız, Q.reloadLookup('TheLookupKey')yöntemi kullanabilirsiniz. Bu, istemci tarafı sözlüğündeki karmasını rastgele hale getirecek RegisteredScriptsve DynJSbir dahaki sefere uç noktayı çağırmaya zorlayacaktır.

Uç noktayı aramaya zorlasanız bile DynJS, bunun her zaman arama verilerinin sunucuya yeniden yükleneceği anlamına gelmediğini lütfen unutmayın. Q.reloadLookupBu nedenle, yalnızca istemci tarafından gerçekleştirdiğiniz bazı eylemlerin, örneğin aramanın varlığı için kaydetme hizmetini çağırmanın (dil iletişim kutusundaki Kaydet düğmesini tıklatarak vb.), arama verilerinin kaydedilmesine neden olacağından eminseniz kullanmalısınız geçersiz sunucu tarafı.

Dinamik komut dosyası yöneticisi aramayı önbelleğe aldıysa, veritabanına ulaşmak yerine yalnızca önbelleğe alınan sürümü döndürecektir.

Dinamik komut dosyası yöneticisi, önbelleğe alınan sürümü yalnızca süresi dolduğunda veya " TwoLevelCache.ExpireGroupItems" yöntemi dinamik komut dosyasıyla GroupKey(örneğin, kaynak satırın grup anahtarıyla) çağrıldığında geçersiz kılar. Bir varlığı (Oluşturma/Güncelleme) işleyicisi aracılığıyla güncellediğinizde bu otomatik olarak yapılır Save.

connection.UpdateByIdBir veritabanı tablosunu doğrudan SQL Management Studio gibi bir araç veya , veya yöntemleri aracılığıyla güncellerseniz Dapperönbellek hiçbir zaman geçersiz kılınmaz. Dinamik komut dosyası yöneticisinin, komut dosyalarıyla ilgili bir şeyi değiştirdiğinize dair hiçbir fikri yoktur ve önbelleğe alma için varsayılan süre Infinite.

Peki bir tabloyu manuel olarak güncellerseniz ne yapmalısınız? Önbelleğe alınmış tüm komut dosyalarını kaldıracak şekilde aramanız ExpireGroupItems(önerilir) veya aramanız (önerilmez) gerekir. Tam komut dosyası adını biliyorsanız, yöntemi IDynamicScriptManager.Resetkullanarak belirli bir komut dosyasını da sıfırlayabilirsiniz .IDynamicScriptManager.Changed("{scriptname}")

Şu anda dinamik bir komut dosyasını istemci tarafından sıfırlamanın bir yolu yoktur. Eğer öyle olsaydı, kötü bir kişinin DDOS saldırıları gerçekleştirmesi ve sunucunuzun önbelleğe alınmasını işe yaramaz hale getirmesi kolay olurdu.

Aramalar Ne Zaman Kullanılır?

Aramalar, başlangıçta yalnızca nadiren değişen verilere sahip küçük referans tabloları için tasarlanmıştır; çünkü bunlar, tamamen sunucudan yüklemeye ve hem istemci hem de sunucu tarafında agresif önbelleğe almaya uygun olan tek tablodur.

Göreceli kullanım kolaylıkları, basitlikleri ve muhtemelen eğitimlerimizde sunduğumuz örnekler nedeniyle Serenity'de en çok kötüye kullanılan özelliklerden biri haline geldiler.

Bu nedenle aşağıdaki durumlarda aramayı kullanmayın:

  • Pek çok kayıt içeren bir tablonuz var, örneğin 1000'den fazla kayıt.
  • Bir öğe için Kimlik ve Ad özellikleri dışında birçok ek sütun yüklemeniz gerekir.
  • Tablodaki veriler, özellikle de aramaya dahil edilen sütunlar sık ​​sık değişir.
  • Verilerin güncelliğine ve geçerliliğine güvenmeniz gerekir (önbelleğe alma nedeniyle güncelliğini yitirmiş olabileceğinden).
  • Aramanın hassas veriler içermesi gerekir (tarayıcı önbelleğinde başkaları tarafından görüntülenebilir!)
  • Aramanın parametrelendirilmesi gerekiyor (bunun yerine bir hizmet kullanın).

Peki, yukarıdaki nedenlerden birinden dolayı aramayı kullanamıyorsanız ne yapmalısınız? Cevap hizmetlerdir ve ServiceLookupEditorbunun yerine LookupEditor.

LookupEditor'nin aksine ServiceLookupEditor, eşleşen sonuçları yüklemek için örneğin Liste hizmeti gibi bir hizmet çağrısı kullanır, Aramaları (adın aksini belirtmesine rağmen) veya dinamik komut dosyalarını kullanmaz, hiçbir zaman tüm kayıtları yüklemez ve varsayılan olarak hiçbir şeyi önbelleğe almaz.

Ancak a LookupEditorve a'dan Lookupfarklı olarak sözlük olarak kayıtlara kimlikleriyle erişemez, bir dizideki tüm kayıtları sağlayamaz ve eşzamansız çalışır, dolayısıyla değerini ayarladığınızda bir sonraki satırda seçilen varlığın özelliklerine erişemezsiniz. .

Yeni uygulamalar için, ServiceLookupEditorsık sık değişmeyen çok küçük referans tablolarının dışındakileri tercih edin.

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 ş...