Pustaka klien Elastic Database dengan Entity Framework
Berlaku untuk: Azure SQL Database
Dokumen ini menunjukkan perubahan dalam aplikasi Entity Framework yang diperlukan untuk mengintegrasikan dengan alat Database Elastis. Fokusnya adalah menyusun manajemen peta shard dan perutean tergantung data dengan pendekatan Code First Entity Framework. Tutorial Code First - Database Baru untuk EF berfungsi sebagai contoh yang berjalan di seluruh dokumen ini. Kode sampel yang menyertai dokumen ini adalah bagian dari kumpulan sampel alat database elastis dalam Sampel Visual Studio Code.
Catatan
Artikel ini tidak berlaku untuk Entity Framework Core (EF Core).
Mengunduh dan Menjalankan Kode Sampel
Guna mengunduh kode untuk artikel ini:
- Diperlukan Visual Studio 2012 atau yang lebih baru.
- Unduh Alat DB Elastis untuk Azure SQL - sampel Integrasi Entity Framework. Ekstrak sampel ke lokasi yang Anda pilih.
- Mulai Visual Studio.
- Di Visual Studio, pilih File -> Buka Proyek/Solusi.
- Pada dialog Buka Proyek, buka sampel yang Anda unduh, lalu pilih EntityFrameworkCodeFirst.sln untuk membuka sampel.
Untuk menjalankan sampel, Anda harus membuat tiga database kosong di Azure SQL Database:
- Database Shard Map Manager
- Database Shard 1
- Database Shard 2
Setelah Anda membuat database ini, isi placeholder di Program.cs dengan nama server, nama database, dan info masuk Anda untuk menyambungkan ke database. Buat solusi di Visual Studio. Visual Studio mengunduh paket NuGet yang diperlukan untuk pustaka klien database elastis, Entity Framework, dan penanganan Kesalahan Sementara sebagai bagian dari proses pembuatan solusi. Pastikan bahwa pemulihan paket NuGet diaktifkan untuk solusi Anda. Anda dapat mengaktifkan pengaturan ini dengan mengklik kanan pada file solusi di Penjelajah Solusi Visual Studio.
Alur kerja Entity Framework
Pengembang Entity Framework mengandalkan salah satu dari empat alur kerja berikut untuk membuat aplikasi dan memastikan konsistensi untuk objek aplikasi:
- Code First (Database Baru): Pengembang EF membuat model dalam kode aplikasi, kemudian EF menghasilkan database dari model tersebut.
- Code First (Database yang Ada): Pengembang mengizinkan EF menghasilkan kode aplikasi untuk model dari database yang ada.
- Model Pertama: Pengembang membuat model di perancang EF, kemudian EF membuat database dari model tersebut.
- Database First: Pengembang menggunakan alat EF untuk menyimpulkan model dari database yang ada.
Semua pendekatan ini bergantung pada kelas DbContext guna mengelola koneksi database dan skema database secara transparan untuk aplikasi. Konstruktor yang berbeda pada kelas dasar DbContext memungkinkan berbagai tingkat kontrol atas pembuatan koneksi, bootstrapping database, dan pembuatan skema. Kesulitan dalam alur kerja ini utamanya disebabkan oleh manajemen koneksi database dari EF yang bersinggungan dengan kemampuan manajemen koneksi antarmuka perutean tergantung data dari pustaka klien database elastis.
Asumsi alat database elastis
Untuk definisi istilah, lihat glosarium alat Elastic Database.
Dengan pustaka klien database elastis, Anda menentukan partisi data aplikasi yang disebut shardlet. Shardlet diidentifikasi oleh kunci sharding dan dipetakan ke database tertentu. Aplikasi boleh memiliki database sebanyak yang diperlukan dan mendistribusikan shardlet untuk memberikan kapasitas atau performa yang cukup untuk persyaratan bisnis saat ini. Pemetaan nilai kunci sharding ke database disimpan oleh peta shard yang disediakan oleh API klien database elastis. Kemampuan ini disebut Shard Map Management, atau disingkat SMM. Peta shard juga berfungsi sebagai perantara koneksi database untuk permintaan mengenai kunci sharding. Kemampuan ini disebut perutean tergantung data.
Manajer peta shard melindungi pengguna dari tampilan data shardlet yang tidak konsisten. Hal ini dapat terjadi ketika ada operasi manajemen shardlet yang berlangsung secara bersamaan (seperti memindahkan data dari satu shard ke shard lainnya). Untuk melakukannya, peta shard yang dikelola oleh pustaka klien akan menjadi perantara koneksi database untuk aplikasi. Ini memungkinkan fungsi peta shard untuk secara otomatis memutuskan koneksi database ketika operasi manajemen shard dapat berdampak pada shardlet tempat koneksi dibuat. Pendekatan ini perlu diintegrasikan dengan beberapa fungsionalitas EF, seperti membuat koneksi baru dari yang sudah ada untuk memeriksa keberadaan database. Secara umum, dapat disimpulkan bahwa konstruktor DbContext standar hanya berfungsi dengan baik untuk koneksi database tertutup yang dapat dikloning dengan aman untuk pekerjaan EF. Sedangkan prinsip desain database elastis hanya berfungsi sebagai perantara koneksi terbuka. Anda mungkin mengira bahwa masalah ini dapat diselesaikan dengan menutup koneksi yang diperantarai oleh pustaka klien sebelum menyerahkannya kepada DbContext EF. Namun, dengan menutup koneksi dan mengandalkan EF untuk membukanya kembali, Anda akan melewati proses validasi dan pemeriksaan konsistensi yang dilakukan oleh pustaka. Namun, fungsi migrasi di EF menggunakan koneksi ini untuk mengelola skema database yang mendasarinya dengan cara yang transparan bagi aplikasi. Sebaiknya Anda mempertahankan dan menggabungkan semua kemampuan ini dari pustaka klien database elastis dan EF dalam aplikasi yang sama. Bagian berikut membahas properti dan persyaratan ini secara lebih rinci.
Persyaratan
Saat bekerja dengan pustaka klien database elastis dan Entity Framework API, sebaiknya pertahankan properti berikut ini:
- Peluasan skala: Untuk menambahkan atau menghapus database dari tingkat data aplikasi yang di-shard sesuai kebutuhan untuk permintaan kapasitas aplikasi. Ini berarti kontrol atas pembuatan dan penghapusan database, penggunaan API manajer peta shard database elastis untuk mengelola database, serta pemetaan shardlet.
- Konsistensi: Aplikasi menggunakan sharding, dan menggunakan kemampuan perutean tergantung data dari pustaka klien. Untuk menghindari kerusakan atau hasil kueri yang salah, koneksi diperantarai melalui manajer peta shard. Hal ini juga akan mempertahankan validasi dan konsistensi.
- Kode Pertama: Untuk mempertahankan kenyamanan paradigma pertama kode EF. Dalam Code First, kelas di aplikasi dipetakan secara transparan ke struktur database yang mendasarinya. Kode aplikasi berinteraksi dengan DbSet yang menutupi sebagian besar aspek yang terlibat dalam pemrosesan database yang mendasarinya.
- Skema: Entity Framework menangani pembuatan skema database awal dan evolusi skema berikutnya melalui migrasi. Dengan mempertahankan kemampuan ini, aplikasi Anda dapat beradaptasi dengan mudah seiring berkembangnya data.
Panduan berikut memberi petunjuk terkait cara memenuhi persyaratan ini untuk aplikasi Code First menggunakan alat database elastis.
Perutean tergantung data menggunakan DbContext EF
Koneksi database dengan Entity Framework biasanya dikelola melalui subkelas DbContext. Buat subkelas ini dengan memanfaatkan DbContext. Di sinilah Anda menentukan DbSet yang mengimplementasikan koleksi objek CLR yang didukung database untuk aplikasi Anda. Dalam konteks perutean tergantung data, Anda dapat mengidentifikasi beberapa properti bermanfaat yang tidak selalu berlaku untuk skenario aplikasi code first EF lainnya:
- Database sudah ada dan telah terdaftar dalam peta shard database elastis.
- Skema aplikasi telah diterapkan ke database (dijelaskan di bawah).
- Koneksi perutean tergantung data ke database diperantarai oleh peta shard.
Untuk mengintegrasikan DbContexts dengan perutean tergantung data untuk peluasan skala:
- Buat koneksi database fisik melalui antarmuka klien database elastis dari pengelola peta shard.
- Menggabungkan koneksi dengan subkelas DbContext
- Teruskan koneksi ke kelas dasar DbContext untuk memastikan semua pemrosesan di sisi EF juga berjalan.
Contoh kode berikut mengilustrasikan pendekatan ini. (Kode ini juga ada di proyek Visual Studio yang menyertainya)
public class ElasticScaleContext<T> : DbContext
{
public DbSet<Blog> Blogs { get; set; }
...
// C'tor for data-dependent routing. This call opens a validated connection
// routed to the proper shard by the shard map manager.
// Note that the base class c'tor call fails for an open connection
// if migrations need to be done and SQL credentials are used. This is the reason for the
// separation of c'tors into the data-dependent routing case (this c'tor) and the internal c'tor for new shards.
public ElasticScaleContext(ShardMap shardMap, T shardingKey, string connectionStr)
: base(CreateDDRConnection(shardMap, shardingKey, connectionStr),
true /* contextOwnsConnection */)
{
}
// Only static methods are allowed in calls into base class c'tors.
private static DbConnection CreateDDRConnection(
ShardMap shardMap,
T shardingKey,
string connectionStr)
{
// No initialization
Database.SetInitializer<ElasticScaleContext<T>>(null);
// Ask shard map to broker a validated connection for the given key
SqlConnection conn = shardMap.OpenConnectionForKey<T>
(shardingKey, connectionStr, ConnectionOptions.Validate);
return conn;
}
Poin utama
Konstruktor baru menggantikan konstruktor default di subkelas DbContext
Konstruktor baru mengambil argumen yang diperlukan untuk perutean tergantung data melalui pustaka klien database elastis:
- peta shard untuk mengakses antarmuka perutean tergantung data,
- kunci sharding untuk mengidentifikasi shardlet,
- string koneksi dengan info masuk untuk koneksi perutean tergantung data ke shard.
Panggilan ke konstruktor kelas dasar mengambil rute lain ke metode statis yang melakukan semua langkah yang diperlukan untuk perutean tergantung data.
- Ini menggunakan panggilan OpenConnectionForKey dari antarmuka klien database elastis pada peta shard untuk menghasilkan koneksi terbuka.
- Peta shard menciptakan koneksi terbuka ke shard yang berisi shardlet untuk kunci sharding yang ada.
- Koneksi terbuka ini diteruskan kembali ke konstruktor kelas dasar DbContext untuk menunjukkan bahwa koneksi ini akan digunakan oleh EF, bukannya membiarkan EF membuat koneksi baru secara otomatis. Dengan cara ini koneksi telah ditandai oleh API klien database elastis sehingga dapat menjamin konsistensi pada operasi manajemen peta shard.
Gunakan konstruktor baru untuk subkelas DbContext Anda, jangan gunakan konstruktor default dalam kode Anda. Berikut adalah contoh:
// Create and save a new blog.
Console.Write("Enter a name for a new blog: ");
var name = Console.ReadLine();
using (var db = new ElasticScaleContext<int>(
sharding.ShardMap,
tenantId1,
connStrBldr.ConnectionString))
{
var blog = new Blog { Name = name };
db.Blogs.Add(blog);
db.SaveChanges();
// Display all Blogs for tenant 1
var query = from b in db.Blogs
orderby b.Name
select b;
…
}
Konstruktor baru membuka koneksi ke shard yang menyimpan data untuk shardlet yang diidentifikasi dengan nilai tenantid1. Kode dalam blok penggunaan tidak berubah agar dapat mengakses DbSet untuk blog menggunakan EF pada shard untuk tenantid1. Ini mengubah semantik untuk kode dalam blok penggunaan sedemikian rupa sehingga semua operasi database kini tercakup dalam satu shard tempat tenantid1 disimpan. Misalnya, kueri LINQ melalui blog DbSet hanya akan menampilkan blog yang disimpan di shard saat ini, bukan yang disimpan di shard lainnya.
Penanganan kesalahan sementara
Tim Microsoft Patterns & Practices menerbitkan Blok Aplikasi Penanganan Kesalahan Sementara. Pustakanya digunakan dengan pustaka klien skala elastis yang dikombinasikan dengan EF. Namun, pastikan bahwa setiap pengecualian sementara kembali ke lokasi tempat Anda dapat memastikan bahwa konstruktor baru digunakan setelah kesalahan sementara, sehingga setiap upaya koneksi baru dilakukan menggunakan konstruktor yang Anda ubah. Jika tidak, koneksi ke shard yang tepat tidak dapat dijamin, dan tidak ada jaminan pula bahwa koneksi akan dipertahankan seiring adanya perubahan pada peta shard.
Sampel kode berikut mengilustrasikan bagaimana kebijakan coba lagi SQL dapat digunakan di sekitar konstruktor subkelas DbContext yang baru:
SqlDatabaseUtils.SqlRetryPolicy.ExecuteAction(() =>
{
using (var db = new ElasticScaleContext<int>(
sharding.ShardMap,
tenantId1,
connStrBldr.ConnectionString))
{
var blog = new Blog { Name = name };
db.Blogs.Add(blog);
db.SaveChanges();
…
}
});
SqlDatabaseUtils.SqlRetryPolicy dalam kode di atas didefinisikan sebagai SqlDatabaseTransientErrorDetectionStrategy dengan jumlah coba lagi sebanyak 10, dan 5 detik waktu tunggu antara coba lagi. Pendekatan ini mirip dengan panduan untuk EF dan transaksi yang dimulai pengguna (lihat Batasan pada Strategi Eksekusi Coba Lagi (EF6 dan seterusnya). Kedua situasi ini mengharuskan program aplikasi mengontrol ruang lingkup pengembalian pengecualian sementara: untuk membuka kembali transaksi, atau (seperti yang ditunjukkan) menciptakan kembali konteks dari konstruktor yang tepat yang menggunakan pustaka klien database elastis.
Kebutuhan untuk mengontrol ruang lingkup tempat pengecualian sementara membawa kita kembali juga menghalangi penggunaan SqlAzureExecutionStrategy bawaan yang ada pada EF. SqlAzureExecutionStrategy akan membuka kembali koneksi, tetapi tidak menggunakan OpenConnectionForKey dan karenanya akan melewati semua validasi yang dilakukan sebagai bagian dari panggilan OpenConnectionForKey. Sebagai gantinya, sampel kode menggunakan DefaultExecutionStrategy bawaan yang juga ada pada EF. Berbeda dengan SqlAzureExecutionStrategy, ini berfungsi dengan benar saat digunakan bersama kebijakan coba lagi dari Penanganan Kesalahan Sementara. Kebijakan eksekusi diatur di kelas ElasticScaleDbConfiguration. Perlu diketahui bahwa kami memutuskan untuk tidak menggunakan DefaultSqlExecutionStrategy karena ini akan menyarankan penggunaan SqlAzureExecutionStrategy jika pengecualian sementara terjadi. Hal ini akan menyebabkan perilaku yang salah seperti yang telah dibahas. Untuk informasi selengkapnya tentang berbagai kebijakan coba lagi dan EF, lihat Ketahanan Koneksi di EF.
Penulisan ulang konstruktor
Contoh kode di atas mengilustrasikan penulisan ulang konstruktor default yang diperlukan untuk aplikasi Anda agar dapat menggunakan perutean yang bergantung pada data dengan Kerangka Kerja Entitas. Tabel berikut menggeneralisasi pendekatan ini ke konstruktor lain.
Konstruktor Saat Ini | Konstruktor yang Ditulis Ulang untuk data | Konstruktor Dasar | Catatan |
---|---|---|---|
MyContext() | ElasticScaleContext(ShardMap, TKey) | DbContext(DbConnection, bool) | Koneksi harus berupa fungsi dari peta shard dan kunci perutean tergantung data. Anda harus melewati pembuatan koneksi otomatis oleh EF dan menggunakan peta shard untuk memperantarai koneksi. |
MyContext(string) | ElasticScaleContext(ShardMap, TKey) | DbContext(DbConnection, bool) | Koneksi harus berupa fungsi dari peta shard dan kunci perutean tergantung data. Nama database atau string koneksi tetap tidak berfungsi karena melewati validasi oleh peta shard. |
MyContext(DbCompiledModel) | ElasticScaleContext(ShardMap, TKey, DbCompiledModel) | DbContext(DbConnection, DbCompiledModel, bool) | Koneksi dibuat untuk peta shard dan kunci sharding yang ada dengan model yang disediakan. Model yang dikompilasi diteruskan ke konstruktor dasar. |
MyContext(DbConnection, bool) | ElasticScaleContext(ShardMap, TKey, bool) | DbContext(DbConnection, bool) | Koneksi perlu disimpulkan dari peta shard dan kuncinya. Ini tidak dapat diberikan sebagai input (kecuali jika input itu sudah menggunakan peta shard dan kuncinya). Boolean diteruskan. |
MyContext(string, DbCompiledModel) | ElasticScaleContext(ShardMap, TKey, DbCompiledModel) | DbContext(DbConnection, DbCompiledModel, bool) | Koneksi perlu disimpulkan dari peta shard dan kuncinya. Ini tidak dapat diberikan sebagai input (kecuali jika input itu sudah menggunakan peta shard dan kuncinya). Model yang dikompilasi diteruskan. |
MyContext(ObjectContext, bool) | ElasticScaleContext(ShardMap, TKey, ObjectContext, bool) | DbContext(ObjectContext, bool) | Konstruktor baru harus memastikan bahwa koneksi apa pun di ObjectContext yang diteruskan sebagai input dirutekan kembali ke koneksi yang dikelola oleh Skala Elastis. Pembahasan terperinci tentang ObjectContexts tidak tercakup dalam dokumen ini. |
MyContext(DbConnection, DbCompiledModel, bool) | ElasticScaleContext(ShardMap, TKey, DbCompiledModel, bool) | DbContext(DbConnection, DbCompiledModel, bool); | Koneksi perlu disimpulkan dari peta shard dan kuncinya. Ini tidak dapat diberikan sebagai input (kecuali jika input itu sudah menggunakan peta shard dan kuncinya). Model dan Boolean diteruskan ke konstruktor kelas dasar. |
Penerapan skema shard melalui migrasi EF
Manajemen skema otomatis adalah solusi mudah yang disediakan oleh Entity Framework. Dalam konteks aplikasi menggunakan alat database elastis, sebaiknya pertahankan kemampuan ini agar dapat menyediakan skema secara otomatis ke shard yang baru dibuat ketika database ditambahkan ke aplikasi yang di-shard. Kasus penggunaan utamanya adalah meningkatkan kapasitas di tingkat data untuk aplikasi yang di-shard menggunakan EF. Mengandalkan kemampuan EF untuk manajemen skema mengurangi upaya administrasi database dengan aplikasi pecahan yang dibangun di atas EF.
Penerapan skema melalui migrasi EF berfungsi secara optimal pada koneksi yang belum dibuka. Ini berbeda dengan skenario perutean tergantung data yang mengandalkan koneksi terbuka yang disediakan oleh API klien database elastis. Perbedaan lainnya adalah persyaratan konsistensi: Meskipun Anda ingin memastikan konsistensi untuk semua koneksi perutean tergantung data guna melindungi dari manipulasi peta shard yang serentak, hal ini bukan merupakan masalah pada penerapan skema awal ke database baru yang belum terdaftar dalam peta shard, dan belum dialokasikan untuk menyimpan shardlet. Oleh karena itu, Anda dapat mengandalkan koneksi database reguler untuk skenario ini, bukannya menggunakan perutean tergantung data.
Ini mengarah pada pendekatan di mana penyebaran skema melalui migrasi EF digabungkan erat dengan pendaftaran database baru sebagai shard di peta shard aplikasi. Hal ini bergantung pada prasyarat berikut:
- Database telah dibuat.
- Database kosong - tidak menyimpan skema pengguna dan data pengguna.
- Database belum dapat diakses melalui API klien database elastis untuk perutean tergantung data.
Dengan prasyarat ini, Anda dapat membuat SqlConnection reguler yang belum dibuka guna memulai migrasi EF untuk penerapan skema. Sampel kode berikut mengilustrasikan pendekatan ini.
// Enter a new shard - i.e. an empty database - to the shard map, allocate a first tenant to it
// and kick off EF initialization of the database to deploy schema
public void RegisterNewShard(string server, string database, string connStr, int key)
{
Shard shard = this.ShardMap.CreateShard(new ShardLocation(server, database));
SqlConnectionStringBuilder connStrBldr = new SqlConnectionStringBuilder(connStr);
connStrBldr.DataSource = server;
connStrBldr.InitialCatalog = database;
// Go into a DbContext to trigger migrations and schema deployment for the new shard.
// This requires an un-opened connection.
using (var db = new ElasticScaleContext<int>(connStrBldr.ConnectionString))
{
// Run a query to engage EF migrations
(from b in db.Blogs
select b).Count();
}
// Register the mapping of the tenant to the shard in the shard map.
// After this step, data-dependent routing on the shard map can be used
this.ShardMap.CreatePointMapping(key, shard);
}
Sampel ini menunjukkan metode RegisterNewShard yang mendaftarkan shard pada peta shard, menerapkan skema melalui migrasi EF, dan menyimpan pemetaan kunci sharding ke shard. Ini bergantung pada konstruktor subkelas DbContext (ElasticScaleContext dalam sampel) yang mengambil string koneksi SQL sebagai input. Kode konstruktor sangat ringkas, seperti yang terlihat pada contoh berikut:
// C'tor to deploy schema and migrations to a new shard
protected internal ElasticScaleContext(string connectionString)
: base(SetInitializerForConnection(connectionString))
{
}
// Only static methods are allowed in calls into base class c'tors
private static string SetInitializerForConnection(string connectionString)
{
// You want existence checks so that the schema can get deployed
Database.SetInitializer<ElasticScaleContext<T>>(
new CreateDatabaseIfNotExists<ElasticScaleContext<T>>());
return connectionString;
}
Anda mungkin telah menggunakan versi konstruktor yang diwarisi dari kelas dasar. Namun, kode harus memastikan bahwa penginisialisasi default untuk EF digunakan saat menyambungkan. Oleh karena itu, akan terjadi penggunaan rute lain melalui metode statis sebelum dilakukannya panggilan dalam konstruktor kelas dasar dengan string koneksi. Perlu diketahui bahwa pendaftaran shard harus berjalan di domain atau proses aplikasi yang berbeda untuk memastikan bahwa pengaturan penginisialisasi untuk EF tidak bertentangan.
Batasan
Pendekatan yang diuraikan dalam dokumen ini menerapkan beberapa batasan:
- Aplikasi EF yang menggunakan LocalDb terlebih dahulu harus bermigrasi ke database SQL Server reguler sebelum menggunakan pustaka klien database elastis. Peluasan skala aplikasi melalui sharding dengan Skala Elastis tidak dapat dilakukan dengan LocalDb. Perlu diketahui bahwa LocalDb masih dapat digunakan dalam pengembangan.
- Setiap perubahan pada aplikasi yang menunjukkan adanya perubahan skema database harus melalui migrasi EF pada semua shard. Kode sampel untuk dokumen ini tidak menunjukkan cara untuk melakukan ini. Sebaiknya gunakan Update-Database dengan parameter ConnectionString untuk melakukan iterasi di semua shard; atau ekstrak skrip T-SQL untuk migrasi tertunda menggunakan Update-Database dengan opsi -Script, lalu terapkan skrip T-SQL ke shard Anda.
- Karena adanya permintaan, dapat diasumsikan bahwa semua pemrosesan databasenya ada dalam satu shard, seperti yang diidentifikasi oleh kunci sharding yang disediakan oleh permintaan. Namun, asumsi ini tidak selalu berlaku. Misalnya, ketika tidak mungkin untuk menyediakan kunci sharding. Untuk mengatasi hal ini, library klien menyediakan kelas MultiShardQuery yang mengimplementasikan abstraksi koneksi untuk kueri melalui beberapa shard. Belajar untuk menggunakan MultiShardQuery yang digabungkan dengan EF tidak tercakup dalam pembahasan dokumen ini
Kesimpulan
Melalui langkah-langkah yang diuraikan dalam dokumen ini, aplikasi EF dapat menggunakan kemampuan pustaka klien database elastis untuk perutean tergantung data dengan memfaktorkan ulang konstruktor subkelas DbContext yang digunakan dalam aplikasi EF. Hal ini membatasi perubahan yang diperlukan untuk tempat-tempat di mana kelas DbContext sudah ada. Selain itu, aplikasi EF dapat terus mendapatkan manfaat dari penerapan skema otomatis dengan menggabungkan langkah-langkah yang memanggil migrasi EF yang diperlukan dengan pendaftaran shard dan pemetaan baru di peta shard.
Konten terkait
Belum menggunakan alat database elastis? Lihat Panduan Memulai kami. Jika memiliki pertanyaan, hubungi kami di halaman pertanyaan Tanya Jawab Microsoft untuk SQL Database dan untuk permintaan fitur, tambahkan ide-ide baru atau ambil suara terbanyak untuk ide yang sudah ada di forum umpan balik SQL Database.