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 JOIN. Birleş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.Name. NameŞ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 , Expression, Tablenameniteliğ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 ServerType. PostgresPostgresDialect 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.