Ana içeriğe atla

Varlık Eşleme(Mapping)

  

Serenity, veritabanı tablosu ve sütun adlarını satırlarla eşleştirmek için bazı eşleme nitelikleri sağlar.

Sütun ve Tablo Eşleme Kuralları

Varsayılan olarak, bir satır sınıfının veritabanındaki aynı ada sahip ancak Satır son eki kaldırılmış bir tabloyla eşleştiği kabul edilir.

Bir özelliğin veritabanındaki aynı ada sahip bir sütunla eşleştiği kabul edilir.

Diyelim ki şöyle bir satır tanımımız var:

public class CustomerRow : Row<CustomerRow.RowFields>
{
    public string StreetAddress
    {
        get => fields.StreetAddress[this];
        set => fields.StreetAddress[this] = value;
    }
}

StreetAddressAlanı seçerek bir sorgu yazsaydık CustomerRowaşağıdaki gibi oluşturulur:

SELECT 
T0.StreetAddress AS [StreetAddress] 
FROM Customer T0

CustomerRowCustomerkurallara göre tabloyla eşleşir . Benzer şekilde StreetAddressözellik, adlı bir sütunla eşleşir StreetAddress.

T0Serenity varlıkları tarafından ana tabloya atanan özel bir takma addır.

StreetAddressSütun ana tabloya ( ) ait olduğundan ifadesi ve sütun takma adı ile Customerseçilir .T0.StreetAddress[StreetAddress]

Özellik adı varsayılan olarak sütun takma adı olarak kullanılır

SqlSettings.AutoQuotedIdentifiers Bayrağı

Bazı veritabanı sistemlerinde tanımlayıcılar büyük/küçük harfe duyarlıdır.

Örneğin Postgress'te tırnak içine alınmış tanımlayıcıya sahip bir sütun oluşturursanız "StreetAddress", onu seçerken tırnak kullanmanız gerekir, yazsanız bile SELECT StreetAddress ...(aynı durumda) çalışmaz.

Formu kullanmanız gerekiyor SELECT "StreetAddress".

Bu nedenle Postgres kullanıcıları genellikle küçük harfli tanımlayıcıları tercih eder. Ancak FluentMigrator her zaman tanımlayıcılardan alıntı yapar, bu nedenle tanımlayıcılara parantez/tırnak işareti eklemek için bir geçici çözüme ihtiyacımız var.

Serenity, varsayılan olarak sütun ve tablo adlarını alıntılamaz/parantez içine almaz, ancak bir uyumluluk ayarına sahiptir. SqlSettings.AutoQuotedIdentifiers bayrağı true olarak ayarlanırsa önceki sorgu şöyle görünür:

SELECT 
T0.[StreetAddress] AS [StreetAddress] 
FROM [Customer] T0

falseUyumluluk nedeniyle bu ayar varsayılan olarak Serenity'dedir, ancak Serene ve StartSharp bunu truedosyada bu şekilde ayarlamıştır Startup.cs.

Postgress lehçesini kullansaydık çıktı şöyle olurdu:

SELECT 
T0."StreetAddress" AS "StreetAddress"
FROM "Customer" T0

Serenity, Serenity.Data.Mapping ad alanında varlıklar ve bunların özelliklerinin veritabanındaki karşılık gelen tablolarla eşlemelerini ayarlamak için kullanılabilecek bir dizi nitelik sağlar .

Aşağıda en sık kullanılanlardan bahsedeceğiz.

Sütun Özniteliği

Column niteliğini kullanarak bir özelliği veritabanındaki başka bir sütun adıyla eşleyebilirsiniz :

public class CustomerRow : Row<CustomerRow.RowFields>
{
    [Column("street_address")]
    public string StreetAddress
    {
        get => fields.StreetAddress[this];
        set => fields.StreetAddress[this] = value;
    }
}

Şimdi sorgu şöyle olur:

SELECT 
T0.street_address AS [StreetAddress] 
FROM Customer T0

Parantezleri manuel olarak eklemek de mümkündür:

public class CustomerRow : Row<CustomerRow.RowFields>
{
    [Column("[street_address]")]
    public string StreetAddress
    {
        get => fields.StreetAddress[this];
        set => fields.StreetAddress[this] = value;
    }
}
SELECT 
T0.[street_address] AS [StreetAddress] 
FROM Customer T0

SqlSettings.AutoQuotedIdentifiers doğruysa köşeli parantezler otomatik olarak dahil edilir.

[]Birden çok veritabanı türüyle çalışmanız gerekiyorsa SqlServer'a özgü parantezleri ( ) kullanın . Bu parantezler, sorguları çalıştırmadan önce lehçeye özgü tırnak işaretlerine (çift tırnak, geri tırnak vb.) dönüştürülür.

Ancak yalnızca tek bir veritabanı türünü hedefliyorsanız, o veritabanı türüne özel alıntılar kullanmayı tercih edebilirsiniz.

TabloAdı Özniteliği

Veritabanındaki tablo adı satır sınıfı adından farklıysa TableName niteliğini kullanın:

[TableName("TheCustomers")]
public class CustomerRow : Row<CustomerRow.RowFields>
{
    public string StreetAddress
    {
        get => fields.StreetAddress[this];
        set => fields.StreetAddress[this] = value;
    }
}
SELECT 
T0.StreetAddress AS [StreetAddress] 
FROM TheCustomers T0

Ayrıca parantez veya tırnak işareti de kullanabilirsiniz:

[TableName("[My Customers]")]
public class CustomerRow : Row<CustomerRow.RowFields>
{
    public string StreetAddress
    {
        get => fields.StreetAddress[this];
        set => fields.StreetAddress[this] = value;
    }
}
SELECT 
T0.StreetAddress AS [StreetAddress] 
FROM [My Customers] T0

Yine veritabanı uyumluluğu için parantezleri tercih edin

İfade Özniteliği

İfade niteliği, temel olmayan bir alanın, örneğin tabloda bulunmayan bir alanın ifadesini belirtmek için kullanılır .

Bu tür alanların birkaç türü olabilir.

Bunun bir örneği, gibi hesaplanmış bir ifadeye sahip Tam Ad alanıdır (T0.[Firstname] + ' ' + T0.[Lastname]).

public class CustomerRow : Row<CustomerRow.RowFields>
{
    public string Firstname
    {
        get => fields.Firstname[this];
        set => fields.Firstname[this] = value;
    }
    
    public string Lastname
    {
        get => fields.Lastname[this];
        set => fields.Lastname[this] = value;
    }
    
    [Expression("(T0.[Firstname] + ' ' + T0.[Lastname])")]
    public string Fullname
    {
        get => fields.Fullname[this];
        set => fields.Fullname[this] = value;
    }
}

+SQL Server'a özel olduğundan buradaki operatöre dikkat edin . Birden fazla veritabanını hedeflemek istiyorsanız ifadeyi şu şekilde yazmalısınız:

CONCAT(T0.[Firstname], CONCAT(' ', T0.[Lastname]))

Ad ve Soyadı tablo alanlarıdır (tablodaki gerçek alanlar), ancak bir ifade niteliğine sahip olmasalar bile, temel, örtülü olarak tanımlanmış ifadelere sahiptirler ve (ana tabloya Serenity sorgularında takma ad "T0.Firstname"atanır "T0.Lastname"T0.

Bu belgede a'dan bahsettiğimizde Table Field, veritabanı tablosundaki bir sütuna karşılık gelen alan anlamına gelir.

View Fieldhesaplanmış bir ifadeye sahip bir alan veya SQL görünümlerindeki birleştirmelerden kaynaklanan alanlar gibi başka bir tablodan kaynaklanan bir alan anlamına gelir.

Referans verdiğimiz alanların önüne takma adı Fullnamekullanarak ifadeyi yazdık .T0

Muhtemelen bu önek olmadan da işe yarayacaktır. Ancak onu kullanmak daha iyidir. Birleştirme eklemeye başladığınızda aynı isimde birden fazla alanın olması ve belirsiz sütun hataları yaşanması mümkündür.

Yabancı Anahtar Özelliği

Yabancı Anahtar özelliği, yabancı anahtar sütunlarını belirtmek için kullanılır ve bunların ilişkili olduğu birincil tablo ve birincil alan hakkında bilgi ekler.

public class CustomerRow : Row<CustomerRow.RowFields>
{
    [ForeignKey("Countries", "Id")]
    public string CountryId
    {
        get => fields.CountryId[this];
        set => fields.CountryId[this] = value;
    }
}

CountryIdBurada tablodaki alanın tablodaki alana Customeryabancı anahtara sahip olduğunu belirttik .IdCountries

Yabancı anahtarın veritabanında bulunması gerekmez. Serenity bunu kontrol etmiyor.

Tablo için tanımlanmış bir Row sınıfınız varsa , şu özniteliğe sahip bir özelliğe sahip olduğu sürece Countriestürü doğrudan yapıcıda kullanmak da mümkündür :ForeignKeyCountryRowIdProperty

public class CustomerRow : Row<CustomerRow.RowFields>
{
    [typeof(CountryRow)]
    public string CountryId
    {
        get => fields.CountryId[this];
        set => fields.CountryId[this] = value;
    }
}

public class CountryRow : Row<CountryRow.RowFields>
{
    [IdProperty]
    public string Id
    {
        get => fields.Id[this];
        set => fields.Id[this] = value;
    }
}

[ForeignKey]Serenity , oluşturulan sorguları tek başına etkilemese de bu tür meta bilgilerden yararlanabilir .

YabancıKey, daha sonra göreceğimiz (LeftJoin) özelliği ile birlikte kullanıldığında daha anlamlıdır.

LeftJoinBağlanmak

Veritabanını sorguladığımızda ilişkilerden dolayı birçok birleştirme yapma eğilimindeyiz. Bu birleştirmelerin çoğu LEFT, INNER veya RIGHT birleştirmelerdir, ancak Serenity varlıklarında genellikle LEFT JOIN'leri kullanırsınız.

Veritabanı yöneticileri, birden fazla tablonun birleşimini sorgulamayı kolaylaştırmak ve bu birleştirmelerin tekrar tekrar yazılmasını önlemek için görünümleri tanımlamayı tercih eder.

Serenity varlıkları tıpkı SQL görünümleri gibi kullanılabilir, böylece diğer tablolardan sütunları bir varlığa getirebilir ve sanki bunlar büyük bir birleştirilmiş tablomuş gibi sorgulayabilirsiniz.

Bunu yapmanın yollarından biri LeftJoin özelliğidir.

public class CustomerRow : Row<CustomerRow.RowFields>
{
    [ForeignKey("Cities", "Id"), LeftJoin("c")]
    public int? CityId
    {
        get => fields.CityId[this];
        set => fields.CityId[this] = value;
    }
    
    [Expression("c.[Name]")]
    public string CityName
    {
        get => Fields.CityName[this];
        set => Fields.CityName[this] = value;
    }
}

Burada Şehirlerc tablosuna birleştirilirken takma ad verilmesi gerektiğini ve birleştirme tipinin LEFT JOINBirleştirme ifadesi , özelliğin yardımıyla ONbelirlenir .c.[Id] == T0.[CityId]ForeignKey

Sol birleştirmeler Serenity varlıklarında tercih edilir çünkü tüm kayıtların bir değeri olmasa bile soldaki tablodan ( Müşteriler)CityId alınmasına olanak tanır .

CityNameifadesine sahip bir görünüm alanıdır (Müşteri tablosunun gerçek bir sütunu değildir) c.NameNameŞehirAdı'nın tablodaki alandan kaynaklandığı açık olmalıdır Cities.

Şimdi tüm müşterilerin şehir adlarını seçmek isteseydik sorgu metnimiz şöyle olurdu:

SELECT 
c.Name AS [CityName] 
FROM Customer T0 
LEFT JOIN Cities c ON (c.[Id] = T0.CityId)

CountryIdTabloda bir alanımız yoksa ama şehirlerin Ülke adlarını dolaylı olarak şehir tablosundaki alan Customeraracılığıyla getirmek istiyorsak ?CountryId

public class CustomerRow : Row<CustomerRow.RowFields>
{
    [ForeignKey("Cities", "Id"), LeftJoin("c")]
    public int? CityId
    {
        get => fields.CityId[this];
        set => fields.CityId[this] = value;
    }
    
    [Expression("c.[Name]")]
    public string CityName
    {
        get => fields.CityName[this];
        set => fields.CityName[this] = value;
    }
    
    [Expression("c.[CountryId]"), ForeignKey("Countries", "Id"), LeftJoin("o")]
    public int? CountryId
    {
        get => fields.CountryId[this];
        set => fields.CountryId[this] = value;
    }
    
    [Expression("o.[Name]")]
    public string CountryName
    {
        get => fields.CountryName[this];
        set => fields.CountryName[this] = value;
    }
}

CountryIdBu sefer tablodaki alanda LEFT JOIN yaptık Cities"o"Ülkeler tablosuna takma adı atadık ve namealanı buradan getirdik.

Ayrılmış sözcükler olmadıkları ve varlıktaki diğer birleşimler arasında benzersiz oldukları sürece birleşimlere herhangi bir tablo takma adını atayabilirsiniz. Sergen, jCountry gibi takma adlar üretir, ancak bunları daha kısa ve daha doğal olanlarla yeniden adlandırabilirsiniz.

Tüm Müşterilerin CityNameve alanlarını seçelim :CountryName

SELECT 
c.[Name] AS [CityName],
o.[Name] AS [CountryName] 
FROM Customer T0 
LEFT JOIN Cities c ON (c.[Id] = T0.CityId) 
LEFT JOIN Countries o ON (o.[Id] = c.[CountryId])

FluentSQL bölümünde bu tür sorguların nasıl oluşturulacağını göreceğiz.

Şu ana kadar LeftJoin niteliğini, üzerinde YabancıKey niteliği bulunan özelliklerle birlikte kullandık.

Varlık sınıflarına LeftJoin niteliğini eklemek de mümkündür. Bu, ana varlıkta karşılık gelen bir alan bulunmayan birleştirmeler veya birden fazla alan içeren birleştirmeler için kullanışlıdır.

CustomerDetailsÖrneğin, müşterilerin bazı ekstra ayrıntılarını (1'e 1 ilişki) saklayan bir genişletme tablonuz olduğunu varsayalım . CustomerDetails tablosunun, aslında tablodaki alanın CustomerIdyabancı anahtarı olan bir birincil anahtarı vardır .IdCustomer

[LeftJoin("cd", "CustomerDetails", "cd.[CustomerId] = T0.[Id]")]
public class CustomerRow : Row<CustomerRow.RowFields>
{
    [Identity, PrimaryKey]
    public int? Id
    {
        get => fields.Id[this];
        set => fields.Id[this] = value;
    }

    [Expression("cd.[DeliveryAddress]")]
    public string DeliveryAddress
    {
        get => return Fields.DeliveryAddress[this];
        set => Fields.DeliveryAddress[this] = value;
    }

Ve burada TeslimatAdresi'ni seçtiğinizde nasıl göründüğü:

SELECT 
cd.[DeliveryAddress] AS [DeliveryAddress] 
FROM Customer T0 
LEFT JOIN CustomerDetails cd ON (cd.[CustomerId] = T0.[Id])

Lehçeye Dayalı İfadeler

Bazen ortak bir ifade kullanmak mümkün olmayabilir. Örneğin Sqlite'ın CONCAT operatörü yoktur. Bunun için , ExpressionTablenameniteliği LeftJoinve diğer bazı nitelikler bir Dialectseçeneği kabul eder.

[DisplayName("FullName"), QuickSearch]
[Expression("CONCAT(T0.[FirstName], CONCAT(' ', T0.[LastName]))")]
[Expression("(T0.FirstName || ' ' || T0.LastName)", Dialect = nameof(ServerType.Sqlite))]
public String FullName
{
    get { return Fields.FullName[this]; }
    set { Fields.FullName[this] = value; }
}

Burada, ilk İfadenin lehçesi olmadığından, bu satıra karşılık gelen bağlantının lehçesine sahip olmadığı sürece, örneğin bir System.Data.Sqlite bağlantısı Sqliteolmadığı sürece, herhangi bir veritabanı türü için kullanılacaktır .

ServerType , ortak sunucu türü adlarını içeren bir numaralandırmadır. Bir dize kullanmak mümkündür, ancak nameofyazım hatalarını önlemek için operatörlü numaralandırmayı tercih ediyoruz.

Bir Satırın Lehçesi Nasıl Belirlenir?

Bir satırın lehçe türünü belirlemek için satırdaki ConnectionKey özniteliği (varsa) kullanılır, aksi takdirde varsayılan lehçe ( SqlSettings.DefaultDialect ) kullanılır.

RowFieldsBir alanın ifadesi , tüm satır örnekleri tarafından paylaşılan bir örnek ilk oluşturulurken belirlenir (sabitlenir) .

Birden fazla lehçeyi belirtmek de mümkündür:

[DisplayName("FullName"), QuickSearch]
[Expression("CONCAT(T0.[FirstName], CONCAT(' ', T0.[LastName]))")]
[Expression("T0.[FirstName] + ' ' + T0.[LastName]", 
     Dialect = "SqlServer2000,SqlServer2005")]
[Expression("(T0.FirstName || ' ' || T0.LastName)", 
     Dialect = "Sqlite,MySql,Postgres")]
public String FullName
{
    get { return Fields.FullName[this]; }
    set { Fields.FullName[this] = value; }
}

Lehçe Eşleştirme

ISqlDialect arayüzünün bir özelliği vardır ServerTypePostgresPostgresDialect içindir ve SqlServeriçin .SqlServer2012DialectSqlServer2008DialectSqlServer2005Dialect

ServerTypeBir ifade lehçesinin bir bağlantı lehçesiyle eşleşmesi için, bağlantı lehçesinin sınıf adıyla ve/veya sınıf adıyla başlaması gerekir (örn. SqlServer2012Dialect).

Hedeflenen ifadeyle birden fazla lehçe türü eşleşirse en uzun ada sahip olan kazanır.

Diyelim ki şu iki ifadeyi yazdık:

[Expression("CONCAT(T0.[FirstName], T0.[LastName])", Dialect = "SqlServer")]
[Expression("T0.[FirstName] + T0.[LastName]", Dialect = "SqlServer200")]

Bağlantı lehçesi ise SqlServer2008her iki ifade de eşleşecektir ancak SqlServer200eşleşmeden daha uzun olduğu için SqlServerikinci ifade kullanılacaktır.

Bağlantı lehçesi ise SqlServer2012yalnızca ilk ifade eşleşir.

!Bir lehçe adı belirtirken, lehçe adını bir ünlem işaretiyle ( ) başlatarak veya şunu kullanarak eşleşmeyi reddetmek de mümkündür NegateDialect:

[Expression("SomeExpression", Dialect = "!SqlServer")]
[Expression("SomeExpression", Dialect = nameof(ServerTypes.SqlServer), NegateDialect = true)]

Yukarıdaki ifadeler yalnızca eşleşmeyen lehçeler için geçerli olacaktır SqlServer.

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