Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Ketika aspek perlu dikonfigurasi dengan cara yang sama di beberapa jenis entitas, teknik berikut memungkinkan untuk mengurangi duplikasi kode dan mengonsolidasikan logika.
Lihat proyek sampel lengkap yang berisi cuplikan kode yang disajikan di bawah ini.
Konfigurasi massal di OnModelCreating
Setiap objek penyusun yang dikembalikan dari ModelBuilder memiliki properti Model atau Metadata yang memberikan akses tingkat rendah ke objek-objek yang membentuk model tersebut. Secara khusus, ada metode yang memungkinkan Anda melakukan iterasi atas objek tertentu dalam model dan menerapkan konfigurasi umum padanya.
Dalam contoh berikut, model berisi jenis nilai kustom Currency:
public readonly struct Currency
{
public Currency(decimal amount)
=> Amount = amount;
public decimal Amount { get; }
public override string ToString()
=> $"${Amount}";
}
Properti jenis ini tidak ditemukan secara default karena penyedia EF saat ini tidak tahu cara memetakannya ke jenis database. Cuplikan OnModelCreating ini menambahkan semua properti jenis Currency dan mengonfigurasi pengonversi nilai ke jenis yang didukung - decimal:
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var propertyInfo in entityType.ClrType.GetProperties())
{
if (propertyInfo.PropertyType == typeof(Currency))
{
entityType.AddProperty(propertyInfo)
.SetValueConverter(typeof(CurrencyConverter));
}
}
}
public class CurrencyConverter : ValueConverter<Currency, decimal>
{
public CurrencyConverter()
: base(
v => v.Amount,
v => new Currency(v))
{
}
}
Kelemahan API Metadata
- Tidak seperti Fluent API, setiap modifikasi pada model perlu dilakukan secara eksplisit. Misalnya, jika beberapa
Currencyproperti dikonfigurasi sebagai navigasi oleh konvensi maka Anda harus terlebih dahulu menghapus navigasi yang merujuk properti CLR sebelum menambahkan properti jenis entitas untuk properti tersebut. #9117 akan meningkatkan ini. - Proses konvensi dilakukan setelah setiap perubahan. Jika Anda menghapus navigasi yang ditemukan oleh konvensi, maka konvensi akan dijalankan lagi dan mungkin akan menambahkannya kembali. Untuk mencegah hal ini terjadi, Anda harus menunda konvensi hingga setelah properti ditambahkan dengan memanggil DelayConventions() dan kemudian membuang objek yang dikembalikan atau untuk menandai properti CLR sebagai diabaikan menggunakan AddIgnored.
- Jenis entitas mungkin ditambahkan setelah perulangan ini terjadi dan konfigurasi tidak akan diterapkan padanya. Ini biasanya dapat dicegah dengan menempatkan kode ini di akhir
OnModelCreating, tetapi jika Anda memiliki dua set konfigurasi yang saling bergantung mungkin tidak ada urutan yang akan memungkinkan mereka diterapkan secara konsisten.
Konfigurasi sebelum konvensi
EF Core memungkinkan konfigurasi pemetaan ditentukan sekali untuk jenis CLR tertentu; konfigurasi tersebut kemudian diterapkan ke semua properti jenis tersebut dalam model saat ditemukan. Ini disebut "konfigurasi model pra-konvensi", karena mengonfigurasi aspek model sebelum konvensi pembuatan model diizinkan untuk dijalankan. Konfigurasi tersebut diterapkan dengan menimpa ConfigureConventions pada jenis yang diturunkan dari DbContext.
Contoh ini menunjukkan cara mengonfigurasi semua properti jenis Currency untuk memiliki pengonversi nilai:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder
.Properties<Currency>()
.HaveConversion<CurrencyConverter>();
}
Dan contoh ini menunjukkan cara mengonfigurasi beberapa faset pada semua properti jenis string:
configurationBuilder
.Properties<string>()
.AreUnicode(false)
.HaveMaxLength(1024);
Nota
Jenis yang ditentukan dalam panggilan dari ConfigureConventions dapat berupa jenis dasar, antarmuka, atau definisi jenis generik. Semua konfigurasi yang cocok akan diterapkan secara berurutan dari yang paling tidak spesifik:
- Antarmuka
- Tipe dasar
- Definisi jenis generik
- Jenis nilai yang tidak dapat diubah ke null
- Jenis yang tepat
Penting
Konfigurasi pra-konvensi setara dengan konfigurasi eksplisit yang diterapkan segera setelah objek yang cocok ditambahkan ke model. Ini akan mengambil alih semua konvensi dan Anotasi Data. Misalnya, dengan konfigurasi di atas semua properti kunci asing string akan dibuat sebagai non-unicode dengan `MaxLength` 1024, bahkan jika ini tidak sesuai dengan kunci utama.
Mengabaikan jenis
Konfigurasi pra-konvensi juga memungkinkan untuk mengabaikan jenis dan mencegahnya ditemukan oleh konvensi baik sebagai jenis entitas atau sebagai properti pada jenis entitas:
configurationBuilder
.IgnoreAny(typeof(IList<>));
Jenis pemetaan standar
Umumnya, EF dapat menerjemahkan kueri dengan konstanta jenis yang tidak didukung oleh penyedia, selama Anda telah menentukan pengonversi nilai untuk properti jenis ini. Namun, dalam kueri yang tidak melibatkan properti apa pun dari jenis ini, tidak ada cara bagi EF untuk menemukan pengonversi nilai yang benar. Dalam hal ini, dimungkinkan untuk memanggil DefaultTypeMapping untuk menambahkan atau mengambil alih pemetaan jenis penyedia:
configurationBuilder
.DefaultTypeMapping<Currency>()
.HasConversion<CurrencyConverter>();
Keterbatasan konfigurasi sebelum konvensi
- Banyak aspek tidak dapat dikonfigurasi dengan pendekatan ini. #6787 akan memperluas ini ke lebih banyak jenis.
- Saat ini konfigurasi hanya ditentukan oleh tipe CLR. #20418 akan memungkinkan predikat kustom.
- Konfigurasi ini dilakukan sebelum model dibuat. Jika ada konflik yang muncul saat menerapkannya, jejak tumpukan pengecualian tidak akan berisi
ConfigureConventionsmetode , sehingga mungkin lebih sulit untuk menemukan penyebabnya.
Konvensi
Konvensi pembuatan model EF Core adalah kelas yang berisi logika yang dipicu berdasarkan perubahan yang dilakukan pada model saat sedang dibangun. Ini membuat model tetap up-to-date saat konfigurasi eksplisit dibuat, atribut pemetaan diterapkan, dan konvensi lainnya berjalan. Untuk berpartisipasi dalam hal ini, setiap konvensi mengimplementasikan satu atau beberapa antarmuka yang menentukan kapan metode yang sesuai akan dipicu. Misalnya, konvensi yang menerapkan IEntityTypeAddedConvention akan dipicu setiap kali jenis entitas baru ditambahkan ke model. Demikian juga, konvensi yang mengimplementasikan keduanya IForeignKeyAddedConvention dan IKeyAddedConvention akan dipicu setiap kali kunci atau kunci asing ditambahkan ke model.
Konvensi pembuatan model adalah cara yang ampuh untuk mengontrol konfigurasi model, tetapi bisa kompleks dan sulit untuk disempurnakan. Dalam banyak kasus, konfigurasi model pra-konvensi dapat digunakan sebagai gantinya untuk dengan mudah menentukan konfigurasi umum untuk properti dan jenis.
Menambahkan konvensi baru
Contoh: Batasi panjang properti diskriminator
Strategi pemetaan warisan tabel per hierarki memerlukan kolom diskriminator untuk menentukan jenis mana yang diwakili dalam baris tertentu. Secara default, EF menggunakan kolom string yang tidak terbatas untuk diskriminator, yang memastikan bahwa itu akan berfungsi untuk panjang diskriminator apa pun. Namun, membatasi panjang maksimum string diskriminator dapat membuat penyimpanan dan kueri yang lebih efisien. Mari kita buat konvensi baru yang akan melakukan itu.
Konvensi pembuatan model EF Core dipicu berdasarkan perubahan yang dilakukan pada model saat sedang dibangun. Ini membuat model tetap up-to-date saat konfigurasi eksplisit dibuat, atribut pemetaan diterapkan, dan konvensi lainnya berjalan. Untuk berpartisipasi dalam hal ini, setiap konvensi mengimplementasikan satu atau beberapa antarmuka yang menentukan kapan konvensi akan dipicu. Misalnya, konvensi yang menerapkan IEntityTypeAddedConvention akan dipicu setiap kali jenis entitas baru ditambahkan ke model. Demikian juga, konvensi yang mengimplementasikan keduanya IForeignKeyAddedConvention dan IKeyAddedConvention akan dipicu setiap kali kunci atau kunci asing ditambahkan ke model.
Mengetahui antarmuka mana yang akan diterapkan bisa sulit, karena konfigurasi yang dibuat untuk model pada satu titik dapat diubah atau dihapus di titik selanjutnya. Misalnya, kunci dapat dibuat oleh konvensi, tetapi kemudian diganti ketika kunci yang berbeda dikonfigurasi secara eksplisit.
Mari kita membuatnya lebih konkret dengan mencoba untuk pertama kali menerapkan konvensi panjang pembeda:
public class DiscriminatorLengthConvention1 : IEntityTypeBaseTypeChangedConvention
{
public void ProcessEntityTypeBaseTypeChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionEntityType? newBaseType,
IConventionEntityType? oldBaseType,
IConventionContext<IConventionEntityType> context)
{
var discriminatorProperty = entityTypeBuilder.Metadata.FindDiscriminatorProperty();
if (discriminatorProperty != null
&& discriminatorProperty.ClrType == typeof(string))
{
discriminatorProperty.Builder.HasMaxLength(24);
}
}
}
Konvensi ini menerapkan IEntityTypeBaseTypeChangedConvention, yang berarti akan dipicu setiap kali hierarki warisan yang dipetakan untuk jenis entitas diubah. Konvensi kemudian menemukan dan mengonfigurasi properti diskriminator string untuk hierarki.
Konvensi ini kemudian digunakan dengan memanggil Add di ConfigureConventions:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Add(_ => new DiscriminatorLengthConvention1());
}
Nota
Daripada menambahkan instans konvensi secara langsung, metode Add ini menerima fungsi pembuat untuk membuat instans konvensi. Ini memungkinkan konvensi untuk menggunakan dependensi dari penyedia layanan internal EF Core. Karena konvensi ini tidak memiliki dependensi, parameter penyedia layanan diberi nama _, menunjukkan bahwa itu tidak pernah digunakan.
Membangun model dan melihat jenis entitas Post menunjukkan bahwa proses ini berhasil - properti diskriminator sekarang dikonfigurasi untuk panjang maksimum 24:
Discriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(24)
Tetapi apa yang terjadi jika kita sekarang secara eksplisit mengonfigurasi properti diskriminator yang berbeda? Contohnya:
modelBuilder.Entity<Post>()
.HasDiscriminator<string>("PostTypeDiscriminator")
.HasValue<Post>("Post")
.HasValue<FeaturedPost>("Featured");
Melihat tampilan debug model, kami menemukan bahwa panjang diskriminator tidak lagi dikonfigurasi.
PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw
Ini karena properti diskriminator yang kami konfigurasi dalam konvensi kami kemudian dihapus ketika diskriminator kustom ditambahkan. Kami dapat mencoba memperbaikinya dengan menerapkan antarmuka lain pada konvensi kami untuk bereaksi terhadap perubahan diskriminator, tetapi mencari tahu antarmuka mana yang akan diterapkan tidak mudah.
Untungnya, ada pendekatan yang lebih mudah. Seringkali, tidak menjadi masalah bagaimana model terlihat saat sedang dibangun, asalkan model akhirnya benar. Selain itu, konfigurasi yang ingin kita terapkan sering kali tidak perlu memicu konvensi lain untuk bereaksi. Oleh karena itu, konvensi kami dapat menerapkan IModelFinalizingConvention.
Konvensi finalisasi model dijalankan setelah pembangunan model lainnya selesai, sehingga memiliki akses ke status model yang hampir selesai. Ini berlawanan dengan konvensi interaktif yang bereaksi terhadap setiap perubahan model dan memastikan bahwa model up-to-date pada titik mana pun dari OnModelCreating eksekusi metode. Konvensi finalisasi model biasanya akan mengiterasi seluruh model sambil mengonfigurasi elemen-elemen model tersebut. Jadi, dalam hal ini, kita akan menemukan setiap diskriminator dalam model dan mengonfigurasinya:
public class DiscriminatorLengthConvention2 : IModelFinalizingConvention
{
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()
.Where(entityType => entityType.BaseType == null))
{
var discriminatorProperty = entityType.FindDiscriminatorProperty();
if (discriminatorProperty != null
&& discriminatorProperty.ClrType == typeof(string))
{
discriminatorProperty.Builder.HasMaxLength(24);
}
}
}
}
Setelah membangun model dengan konvensi baru ini, kami menemukan bahwa panjang diskriminator sekarang dikonfigurasi dengan benar meskipun telah disesuaikan:
PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(24)
Kita dapat melangkah lebih jauh dan mengonfigurasi panjang maksimum menjadi panjang nilai diskriminator terpanjang:
public class DiscriminatorLengthConvention3 : IModelFinalizingConvention
{
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()
.Where(entityType => entityType.BaseType == null))
{
var discriminatorProperty = entityType.FindDiscriminatorProperty();
if (discriminatorProperty != null
&& discriminatorProperty.ClrType == typeof(string))
{
var maxDiscriminatorValueLength =
entityType.GetDerivedTypesInclusive().Select(e => ((string)e.GetDiscriminatorValue()!).Length).Max();
discriminatorProperty.Builder.HasMaxLength(maxDiscriminatorValueLength);
}
}
}
}
Sekarang panjang maks kolom diskriminator adalah 8, yang merupakan panjang "Unggulan", nilai diskriminator terpanjang yang digunakan.
PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(8)
Contoh: Panjang default untuk semua properti string
Mari kita lihat contoh lain di mana konvensi finalisasi dapat digunakan - mengatur panjang maksimum default untuk properti string apa pun . Konvensi ini terlihat sangat mirip dengan contoh sebelumnya:
public class MaxStringLengthConvention : IModelFinalizingConvention
{
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var property in modelBuilder.Metadata.GetEntityTypes()
.SelectMany(
entityType => entityType.GetDeclaredProperties()
.Where(
property => property.ClrType == typeof(string))))
{
property.Builder.HasMaxLength(512);
}
}
}
Konvensi ini cukup sederhana. Ini menemukan setiap properti string dalam model dan mengatur panjang maksimumnya menjadi 512. Melihat tampilan debug di properti untuk Post, kita melihat bahwa semua properti string sekarang memiliki panjang maksimum 512.
EntityType: Post
Properties:
Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
AuthorId (no field, int?) Shadow FK Index
BlogId (no field, int) Shadow Required FK Index
Content (string) Required MaxLength(512)
Discriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(512)
PublishedOn (DateTime) Required
Title (string) Required MaxLength(512)
Nota
Hal yang sama dapat dicapai dengan konfigurasi pra-konvensi, tetapi menggunakan konvensi memungkinkan untuk memfilter properti yang berlaku lebih lanjut dan untuk Anotasi Data untuk mengambil alih konfigurasi.
Akhirnya, sebelum kita meninggalkan contoh ini, apa yang terjadi jika kita menggunakan baik MaxStringLengthConvention maupun DiscriminatorLengthConvention3 pada saat yang sama? Jawabannya adalah bahwa hal ini tergantung pada urutan penambahan mereka, karena konvensi penyelesaian model berjalan dalam urutan yang ditambahkan. Jadi jika MaxStringLengthConvention ditambahkan terakhir, maka akan berjalan terakhir, dan akan mengatur panjang maksimum properti diskriminator ke 512. Oleh karena itu, dalam hal ini, lebih baik menambahkan DiscriminatorLengthConvention3 yang terakhir sehingga dapat mengambil alih panjang maks default hanya untuk properti diskriminator, sambil meninggalkan semua properti string lainnya sebagai 512.
Mengganti konvensi yang ada
Terkadang daripada menghapus konvensi yang ada sepenuhnya, kita malah ingin menggantinya dengan konvensi yang pada dasarnya melakukan hal yang sama, tetapi dengan perilaku yang berubah. Ini berguna karena konvensi yang ada telah mengimplementasikan antarmuka yang diperlukan untuk dipicu secara tepat.
Contoh: Pemetaan properti dengan sistem keikutsertaan
EF Core memetakan semua properti baca-tulis publik berdasarkan konvensi. Ini mungkin tidak sesuai dengan cara jenis entitas Anda ditentukan. Untuk mengubah ini, kita dapat mengganti PropertyDiscoveryConvention dengan implementasi kita sendiri yang tidak memetakan properti apa pun kecuali jika secara eksplisit dipetakan dalam OnModelCreating atau ditandai dengan atribut baru yang disebut Persist:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public sealed class PersistAttribute : Attribute
{
}
Berikut adalah konvensi baru:
public class AttributeBasedPropertyDiscoveryConvention : PropertyDiscoveryConvention
{
public AttributeBasedPropertyDiscoveryConvention(ProviderConventionSetBuilderDependencies dependencies)
: base(dependencies)
{
}
public override void ProcessEntityTypeAdded(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionContext<IConventionEntityTypeBuilder> context)
=> Process(entityTypeBuilder);
public override void ProcessEntityTypeBaseTypeChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionEntityType? newBaseType,
IConventionEntityType? oldBaseType,
IConventionContext<IConventionEntityType> context)
{
if ((newBaseType == null
|| oldBaseType != null)
&& entityTypeBuilder.Metadata.BaseType == newBaseType)
{
Process(entityTypeBuilder);
}
}
private void Process(IConventionEntityTypeBuilder entityTypeBuilder)
{
foreach (var memberInfo in GetRuntimeMembers())
{
if (Attribute.IsDefined(memberInfo, typeof(PersistAttribute), inherit: true))
{
entityTypeBuilder.Property(memberInfo);
}
else if (memberInfo is PropertyInfo propertyInfo
&& Dependencies.TypeMappingSource.FindMapping(propertyInfo) != null)
{
entityTypeBuilder.Ignore(propertyInfo.Name);
}
}
IEnumerable<MemberInfo> GetRuntimeMembers()
{
var clrType = entityTypeBuilder.Metadata.ClrType;
foreach (var property in clrType.GetRuntimeProperties()
.Where(p => p.GetMethod != null && !p.GetMethod.IsStatic))
{
yield return property;
}
foreach (var property in clrType.GetRuntimeFields())
{
yield return property;
}
}
}
}
Petunjuk / Saran
Saat mengganti konvensi bawaan, implementasi konvensi baru harus diwarisi dari kelas konvensi yang ada. Perhatikan bahwa beberapa konvensi memiliki implementasi relasional atau khusus penyedia, dalam hal ini implementasi konvensi baru harus mewarisi dari kelas konvensi yang paling spesifik yang ada untuk penyedia database yang digunakan.
Konvensi kemudian didaftarkan menggunakan Replace metode dalam ConfigureConventions:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Replace<PropertyDiscoveryConvention>(
serviceProvider => new AttributeBasedPropertyDiscoveryConvention(
serviceProvider.GetRequiredService<ProviderConventionSetBuilderDependencies>()));
}
Petunjuk / Saran
Ini adalah kasus di mana konvensi yang ada memiliki dependensi, yang diwakili oleh ProviderConventionSetBuilderDependencies objek dependensi. Ini diperoleh dari penyedia layanan internal menggunakan GetRequiredService dan diteruskan ke konstruktor konvensi.
Perhatikan bahwa konvensi ini memungkinkan kolom dipetakan (selain properti) asalkan mereka ditandai dengan [Persist]. Ini berarti kita dapat menggunakan bidang privat sebagai kunci tersembunyi dalam model.
Misalnya, pertimbangkan jenis entitas berikut:
public class LaundryBasket
{
[Persist]
[Key]
private readonly int _id;
[Persist]
public int TenantId { get; init; }
public bool IsClean { get; set; }
public List<Garment> Garments { get; } = new();
}
public class Garment
{
public Garment(string name, string color)
{
Name = name;
Color = color;
}
[Persist]
[Key]
private readonly int _id;
[Persist]
public int TenantId { get; init; }
[Persist]
public string Name { get; }
[Persist]
public string Color { get; }
public bool IsClean { get; set; }
public LaundryBasket? Basket { get; set; }
}
Model yang dibangun dari jenis entitas ini adalah:
Model:
EntityType: Garment
Properties:
_id (_id, int) Required PK AfterSave:Throw ValueGenerated.OnAdd
Basket_id (no field, int?) Shadow FK Index
Color (string) Required
Name (string) Required
TenantId (int) Required
Navigations:
Basket (LaundryBasket) ToPrincipal LaundryBasket Inverse: Garments
Keys:
_id PK
Foreign keys:
Garment {'Basket_id'} -> LaundryBasket {'_id'} ToDependent: Garments ToPrincipal: Basket ClientSetNull
Indexes:
Basket_id
EntityType: LaundryBasket
Properties:
_id (_id, int) Required PK AfterSave:Throw ValueGenerated.OnAdd
TenantId (int) Required
Navigations:
Garments (List<Garment>) Collection ToDependent Garment Inverse: Basket
Keys:
_id PK
Biasanya, IsClean akan dipetakan, tetapi karena tidak ditandai dengan [Persist], sekarang diperlakukan sebagai properti yang tidak dipetakan.
Petunjuk / Saran
Konvensi ini tidak dapat diimplementasikan sebagai konvensi finalisasi model karena ada konvensi finalisasi model yang perlu dijalankan setelah properti dipetakan untuk mengonfigurasinya lebih lanjut.
Pertimbangan implementasi konvensi
EF Core melacak bagaimana setiap bagian konfigurasi dibuat. Ini diwakili oleh ConfigurationSource enum. Jenis konfigurasi yang berbeda adalah:
-
Explicit: Elemen model dikonfigurasi secara eksplisit diOnModelCreating -
DataAnnotation: Elemen model dikonfigurasi menggunakan atribut pemetaan (alias anotasi data) pada jenis CLR -
Convention: Elemen model dikonfigurasi oleh konvensi pembuatan model
Konvensi tidak boleh mengambil alih konfigurasi yang ditandai sebagai DataAnnotation atau Explicit. Hal ini dicapai dengan menggunakan penyusun konvensi, misalnya, IConventionPropertyBuilder, yang diperoleh dari properti Builder. Contohnya:
property.Builder.HasMaxLength(512);
Panggilan HasMaxLength pada penyusun konvensi hanya akan mengatur panjang maksimum jika belum dikonfigurasi oleh atribut pemetaan atau di OnModelCreating.
Metode penyusun seperti ini juga memiliki parameter kedua: fromDataAnnotation. Atur ini ke true jika konvensi membuat konfigurasi atas nama atribut pemetaan. Contohnya:
property.Builder.HasMaxLength(512, fromDataAnnotation: true);
Ini mengatur ConfigurationSource ke DataAnnotation, yang berarti bahwa nilai sekarang dapat ditimpa oleh pemetaan eksplisit melalui OnModelCreating, tetapi tidak melalui konvensi atribut non-pemetaan.
Jika konfigurasi saat ini tidak dapat diubah maka metode akan mengembalikan null, ini harus diperhatikan jika diperlukan konfigurasi lebih lanjut:
property.Builder.HasMaxLength(512)?.IsUnicode(false);
Perhatikan bahwa jika konfigurasi unicode tidak dapat ditimpa, panjang maksimum tetap akan ditetapkan. Jika Anda perlu mengonfigurasi faset hanya ketika kedua panggilan berhasil, Maka Anda dapat memeriksanya secara preemptive dengan memanggil CanSetMaxLength dan CanSetIsUnicode:
public class MaxStringLengthNonUnicodeConvention : IModelFinalizingConvention
{
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var property in modelBuilder.Metadata.GetEntityTypes()
.SelectMany(
entityType => entityType.GetDeclaredProperties()
.Where(
property => property.ClrType == typeof(string))))
{
var propertyBuilder = property.Builder;
if (propertyBuilder.CanSetMaxLength(512)
&& propertyBuilder.CanSetIsUnicode(false))
{
propertyBuilder.HasMaxLength(512)!.IsUnicode(false);
}
}
}
}
Di sini kita dapat yakin bahwa panggilan ke HasMaxLength tidak akan kembali null. Masih disarankan untuk menggunakan instance pembangun yang dikembalikan dari HasMaxLength karena mungkin berbeda dari propertyBuilder.
Nota
Konvensi lain tidak dipicu segera setelah konvensi membuat perubahan, mereka tertunda sampai semua konvensi selesai memproses perubahan saat ini.
IConventionContext
Semua metode konvensi juga memiliki IConventionContext<TMetadata> parameter . Ini menyediakan metode yang dapat berguna dalam beberapa kasus tertentu.
Contoh: Konvensi NotMappedAttribute
Konvensi ini mencari NotMappedAttribute pada jenis yang ditambahkan ke model dan mencoba menghapus jenis entitas tersebut dari model. Tetapi jika jenis entitas dihapus dari model, maka konvensi lain yang menerapkan ProcessEntityTypeAdded tidak perlu lagi dijalankan. Ini dapat dicapai dengan memanggil StopProcessing():
public virtual void ProcessEntityTypeAdded(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionContext<IConventionEntityTypeBuilder> context)
{
var type = entityTypeBuilder.Metadata.ClrType;
if (!Attribute.IsDefined(type, typeof(NotMappedAttribute), inherit: true))
{
return;
}
if (entityTypeBuilder.ModelBuilder.Ignore(entityTypeBuilder.Metadata.Name, fromDataAnnotation: true) != null)
{
context.StopProcessing();
}
}
IConventionModel
Setiap objek penyusun yang diteruskan ke konvensi mengekspos properti Metadata yang menyediakan akses langsung ke objek-objek yang membentuk model. Secara khusus, ada metode yang memungkinkan Anda melakukan iterasi atas objek tertentu dalam model dan menerapkan konfigurasi umum seperti yang terlihat di Contoh: Panjang default untuk semua properti string. API ini mirip IMutableModel dengan yang ditampilkan dalam konfigurasi massal.
Perhatian
Disarankan untuk selalu melakukan konfigurasi dengan memanggil metode pada builder yang diekspos sebagai properti Builder, karena builder memeriksa apakah konfigurasi yang diberikan akan menggantikan sesuatu yang sudah ditentukan menggunakan Fluent API atau Anotasi Data.
Kapan menggunakan setiap pendekatan untuk konfigurasi massal
Gunakan API Metadata saat:
- Konfigurasi perlu diterapkan pada waktu tertentu dan tidak bereaksi terhadap perubahan model nanti.
- Kecepatan membangun model sangat penting. API Metadata memiliki lebih sedikit pemeriksaan keamanan dan dengan demikian bisa sedikit lebih cepat daripada pendekatan lain, namun menggunakan model yang Dikompilasi akan menghasilkan waktu startup yang lebih baik.
Gunakan Konfigurasi model pra-konvensi saat:
- Kondisi penerapannya sederhana karena hanya tergantung pada jenisnya.
- Konfigurasi perlu diterapkan pada titik mana pun properti dari jenis yang diberikan ditambahkan dalam model dan mengambil alih Anotasi dan konvensi Data
Gunakan Finalisasi Konvensi saat:
- Kondisi penerapannya kompleks.
- Konfigurasi tidak boleh menggantikan yang sudah ditentukan oleh Anotasi Data.
Gunakan Konvensi Interaktif saat:
- Beberapa konvensi bergantung satu sama lain. Konvensi penyelesaian dilakukan sesuai urutan saat ditambahkan dan oleh karena itu tidak dapat bereaksi terhadap perubahan yang dibuat oleh konvensi penyelesaian berikutnya.
- Logika dipakai bersama di antara beberapa konteks. Konvensi interaktif lebih aman daripada pendekatan lainnya.