Fitur baru di EF Core 2.0

.NET Standar 2.0

EF Core sekarang menargetkan .NET Standard 2.0, yang berarti dapat berfungsi dengan .NET Core 2.0, .NET Framework 4.6.1, dan pustaka lain yang mengimplementasikan .NET Standard 2.0. Lihat Implementasi .NET yang didukung untuk detail selengkapnya tentang apa saja yang didukung.

Pemodelan

Pemisahan tabel

Kini pemetaan dua atau beberapa jenis entitas dapat dilakukan ke tabel yang sama tempat kolom kunci primer akan dibagikan dan setiap baris akan sesuai dengan dua atau beberapa entitas.

Untuk menggunakan pemisahan tabel, hubungan pengidentifikasi (tempat properti kunci asing membentuk kunci primer) harus dikonfigurasi di antara semua jenis entitas yang berbagi tabel:

modelBuilder.Entity<Product>()
    .HasOne(e => e.Details).WithOne(e => e.Product)
    .HasForeignKey<ProductDetails>(e => e.Id);
modelBuilder.Entity<Product>().ToTable("Products");
modelBuilder.Entity<ProductDetails>().ToTable("Products");

Baca bagian pemisahan tabel untuk informasi selengkapnya tentang fitur ini.

Jenis yang dimiliki

Jenis entitas yang dimiliki dapat berbagi jenis .NET yang sama dengan jenis entitas lain yang dimiliki, tetapi karena tidak dapat diidentifikasi hanya dengan jenis .NET, harus ada navigasi ke sana dari jenis entitas lain. Entitas yang berisi navigasi penentu adalah entitas pemilik. Saat mengkueri pemilik, jenis yang dimiliki akan disertakan secara default.

Menurut konvensi, kunci primer bayangan akan dibuat untuk jenis yang dimiliki dan akan dipetakan ke tabel yang sama dengan pemiliknya dengan menggunakan pemisahan tabel. Ini memungkinkan penggunaan jenis yang dimiliki, mirip dengan bagaimana jenis kompleks digunakan di EF6:

modelBuilder.Entity<Order>().OwnsOne(p => p.OrderDetails, cb =>
    {
        cb.OwnsOne(c => c.BillingAddress);
        cb.OwnsOne(c => c.ShippingAddress);
    });

public class Order
{
    public int Id { get; set; }
    public OrderDetails OrderDetails { get; set; }
}

public class OrderDetails
{
    public StreetAddress BillingAddress { get; set; }
    public StreetAddress ShippingAddress { get; set; }
}

public class StreetAddress
{
    public string Street { get; set; }
    public string City { get; set; }
}

Baca bagian jenis entitas yang dimiliki untuk informasi selengkapnya tentang fitur ini.

Filter kueri tingkat model

EF Core 2.0 menyertakan fitur baru yang kami sebut sebagai Filter kueri tingkat model. Fitur ini memungkinkan predikat kueri LINQ (ekspresi boolean yang biasanya diteruskan ke operator kueri LINQ Where) untuk didefinisikan secara langsung pada Jenis Entitas dalam model metadata (biasanya di OnModelCreating). Filter tersebut otomatis diterapkan ke kueri LINQ apa pun yang melibatkan Jenis Entitas tersebut, termasuk Jenis Entitas yang direferensikan secara tidak langsung, seperti melalui penggunaan referensi properti Include atau navigasi langsung. Beberapa aplikasi umum dari fitur ini adalah:

  • Penghapusan sementara - Jenis Entitas mendefinisikan properti IsDeleted.
  • Multi-penyewaan - Jenis Entitas menentukan properti TenantId.

Berikut adalah contoh sederhana yang menunjukkan fitur untuk dua skenario yang tercantum di atas:

public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    public int TenantId { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>().HasQueryFilter(
            p => !p.IsDeleted
            && p.TenantId == this.TenantId);
    }
}

Kami mendefinisikan filter tingkat model yang mengimplementasikan multi-penyewaan dan penghapusan sementara untuk instans Jenis Entitas Post. Perhatikan penggunaan properti tingkat instans DbContext: TenantId. Filter tingkat model akan menggunakan nilai dari instans konteks yang benar (yaitu, instans konteks yang menjalankan kueri).

Filter dapat dinonaktifkan untuk kueri LINQ individual menggunakan operator IgnoreQueryFilters().

Batasan

  • Referensi navigasi tidak diperbolehkan. Fitur ini dapat ditambahkan berdasarkan umpan balik.
  • Filter hanya dapat ditentukan untuk Jenis Entitas akar hierarki.

Pemetaan fungsi skalar database

EF Core 2.0 mencakup kontribusi penting dari Paul Middleton yang memungkinkan pemetaan fungsi skalar database ke stub metode sehingga dapat digunakan dalam kueri LINQ dan diterjemahkan ke SQL.

Berikut adalah deskripsi singkat tentang bagaimana fitur tersebut dapat digunakan:

Nyatakan metode statis pada DbContext Anda dan beri anotasi dengan DbFunctionAttribute:

public class BloggingContext : DbContext
{
    [DbFunction]
    public static int PostReadCount(int blogId)
    {
        throw new NotImplementedException();
    }
}

Metode seperti ini terdaftar secara otomatis. Setelah terdaftar, panggilan ke metode dalam kueri LINQ dapat diterjemahkan ke panggilan fungsi di SQL:

var query =
    from p in context.Posts
    where BloggingContext.PostReadCount(p.Id) > 5
    select p;

Beberapa hal yang perlu diperhatikan:

  • Menurut konvensi, nama metode digunakan sebagai nama fungsi (dalam hal ini fungsi yang ditentukan pengguna) saat membuat SQL, tetapi Anda dapat mengganti nama dan skema selama pendaftaran metode.
  • Saat ini hanya fungsi skalar yang didukung.
  • Anda harus membuat fungsi yang dipetakan dalam database. Migrasi EF Core tidak akan menangani pembuatannya.

Konfigurasi jenis mandiri untuk kode terlebih dahulu

Di EF6, konfigurasi kode terlebih dahulu dari jenis entitas tertentu dapat disertakan dengan mengambil dari EntityTypeConfiguration. Di EF Core 2.0 kami menghadirkan pola ini kembali:

class CustomerConfiguration : IEntityTypeConfiguration<Customer>
{
    public void Configure(EntityTypeBuilder<Customer> builder)
    {
        builder.HasKey(c => c.AlternateKey);
        builder.Property(c => c.Name).HasMaxLength(200);
    }
}

...
// OnModelCreating
builder.ApplyConfiguration(new CustomerConfiguration());

Performa Tinggi

Pengumpulan DbContext

Pola dasar untuk menggunakan EF Core di aplikasi ASP.NET Core biasanya melibatkan pendaftaran jenis DbContext kustom ke dalam sistem injeksi dependensi dan kemudian mendapatkan instans jenis tersebut melalui parameter konstruktor di pengontrol. Artinya instans DbContext baru dibuat untuk setiap permintaan.

Di versi 2.0, kami memperkenalkan cara baru untuk mendaftarkan jenis DbContext kustom dalam injeksi dependensi yang secara transparan memasukkan kumpulan instans DbContext yang dapat digunakan kembali. Untuk menggunakan pengumpulan DbContext, gunakan AddDbContextPool alih-alih AddDbContext selama pendaftaran layanan:

services.AddDbContextPool<BloggingContext>(
    options => options.UseSqlServer(connectionString));

Jika metode ini digunakan, pada saat instans DbContext diminta oleh pengontrol, kami akan memeriksa terlebih dahulu apakah ada instans yang tersedia di kumpulan. Setelah pemrosesan permintaan selesai, status apa pun pada instans akan diatur ulang dan instans itu sendiri dikembalikan ke kumpulan.

Secara konseptual, ini mirip dengan cara pengumpulan koneksi beroperasi di penyedia ADO.NET dan keuntungannya adalah menghemat beberapa biaya inisialisasi instans DbContext.

Batasan

Metode baru memperkenalkan beberapa batasan tentang hal-hal yang dapat dilakukan dalam metode OnConfiguring() dari DbContext.

Peringatan

Hindari penggunaan Pengumpulan DbContext jika Anda mempertahankan status Anda sendiri (misalnya, bidang privat) di kelas DbContext turunan yang tidak boleh dibagikan di seluruh permintaan. EF Core hanya akan mengatur ulang status yang diketahuinya sebelum menambahkan instans DbContext ke kumpulan.

Kueri yang dikompilasi secara eksplisit

Ini adalah fitur performa keikutsertaan kedua yang dirancang untuk menawarkan keuntungan dalam skenario skala tinggi.

API kueri yang dikompilasi secara manual atau eksplisit telah tersedia di versi EF sebelumnya dan juga di LINQ ke SQL untuk memungkinkan aplikasi menyimpan terjemahan kueri sehingga hanya dapat dihitung sekali dan dijalankan berkali-kali.

Meskipun secara umum EF Core dapat mengompilasi dan menembolokan kueri secara otomatis berdasarkan representasi hash dari ekspresi kueri, mekanisme ini dapat digunakan untuk memperoleh peningkatan performa kecil dengan melewati komputasi hash dan pencarian cache, memungkinkan aplikasi untuk menggunakan kueri yang sudah dikompilasi melalui permintaan delegasi.

// Create an explicitly compiled query
private static Func<CustomerContext, int, Customer> _customerById =
    EF.CompileQuery((CustomerContext db, int id) =>
        db.Customers
            .Include(c => c.Address)
            .Single(c => c.Id == id));

// Use the compiled query by invoking it
using (var db = new CustomerContext())
{
   var customer = _customerById(db, 147);
}

Pelacakan Perubahan

Melampirkan dapat melacak grafik entitas baru dan yang sudah ada

EF Core mendukung pembuatan nilai kunci otomatis melalui berbagai mekanisme. Saat menggunakan fitur ini, nilai dihasilkan jika properti kunci berupa runtime bahasa umum default--biasanya nol atau null. Ini berarti bahwa grafik entitas dapat diteruskan ke DbContext.Attach atau DbSet.Attach dan EF Core akan menandai entitas yang memiliki kunci yang telah ditetapkan sebagai Unchanged sedangkan entitas yang tidak memiliki kunci yang ditetapkan akan ditandai sebagai Added. Ini memudahkan melampirkan grafik campuran entitas baru dan yang sudah ada saat menggunakan kunci yang dibuat. DbContext.Update dan DbSet.Update berfungsi dengan cara yang sama, kecuali jika entitas dengan kunci yang ditetapkan ditandai sebagai Modified, bukan Unchanged.

Kueri

Terjemahan LINQ yang disempurnakan

Memungkinkan lebih banyak kueri yang berhasil dijalankan, dengan lebih banyak logika yang dievaluasi di dalam database (daripada di dalam memori) dan lebih sedikit data yang tidak perlu diambil dari database.

Penyempurnaan GroupJoin

Ini meningkatkan SQL yang dihasilkan untuk gabungan grup. Gabungan grup paling sering merupakan hasil dari sub-kueri di properti navigasi opsional.

Interpolasi string di FromSql dan ExecuteSqlCommand

C# 6 memperkenalkan Interpolasi String, fitur yang memungkinkan ekspresi C# disematkan langsung dalam literal string, memberikan cara yang bagus untuk membangun string saat runtime. Dalam EF Core 2.0, dukungan khusus ditambahkan untuk string yang terinterpolasi ke dua API utama kami yang menerima string SQL mentah: FromSql dan ExecuteSqlCommand. Dukungan baru ini memungkinkan interpolasi string C# untuk digunakan dengan "aman". Yaitu dengan cara yang melindungi dari kesalahan injeksi SQL umum yang dapat terjadi saat membangun SQL secara dinamis saat runtime.

Ini contohnya:

var city = "London";
var contactTitle = "Sales Representative";

using (var context = CreateContext())
{
    context.Set<Customer>()
        .FromSql($@"
            SELECT *
            FROM ""Customers""
            WHERE ""City"" = {city} AND
                ""ContactTitle"" = {contactTitle}")
            .ToArray();
  }

Dalam contoh ini, ada dua variabel yang disematkan dalam string format SQL. EF Core akan menghasilkan SQL berikut:

@p0='London' (Size = 4000)
@p1='Sales Representative' (Size = 4000)

SELECT *
FROM ""Customers""
WHERE ""City"" = @p0
    AND ""ContactTitle"" = @p1

EF.Functions.Like()

Kami telah menambahkan properti EF.Functions yang dapat digunakan oleh EF Core atau penyedia untuk menentukan metode yang memetakan ke fungsi atau operator database sehingga dapat dipanggil dalam kueri LINQ. Contoh pertama dari metode seperti tersebut adalah Like():

var aCustomers =
    from c in context.Customers
    where EF.Functions.Like(c.Name, "a%")
    select c;

Perhatikan bahwa Like() dilengkapi dengan implementasi dalam memori, yang dapat berguna saat digunakan terhadap database dalam memori atau saat evaluasi predikat perlu dilakukan di sisi klien.

Pengelolaan database

Kait pluralisasi untuk perancah DbContext

EF Core 2.0 memperkenalkan layanan IPluralizer baru yang digunakan untuk membuat singularisasi nama jenis entitas dan pluralisasi nama DbSet. Implementasi defaultnya adalah no-op, jadi ini hanya sebuah pengait tempat orang dapat dengan mudah memasang pluralizer-nya sendiri.

Berikut adalah tampilan pengembang untuk mengaitkan pluralizer-nya sendiri:

public class MyDesignTimeServices : IDesignTimeServices
{
    public void ConfigureDesignTimeServices(IServiceCollection services)
    {
        services.AddSingleton<IPluralizer, MyPluralizer>();
    }
}

public class MyPluralizer : IPluralizer
{
    public string Pluralize(string name)
    {
        return Inflector.Inflector.Pluralize(name) ?? name;
    }

    public string Singularize(string name)
    {
        return Inflector.Inflector.Singularize(name) ?? name;
    }
}

Lainnya

Memindahkan penyedia ADO.NET SQLite ke SQLitePCL.raw

Ini memberi kami solusi yang lebih andal di Microsoft.Data.Sqlite untuk mendistribusikan biner SQLite asli di berbagai platform.

Hanya satu penyedia per model

Secara signifikan menambah cara penyedia dapat berinteraksi dengan model dan menyederhanakan cara kerja konvensi, anotasi, dan API yang lancar dengan berbagai penyedia.

EF Core 2.0 sekarang akan membangun IModel berbeda untuk setiap penyedia berbeda yang digunakan. Ini biasanya transparan untuk aplikasi. Ini telah memfasilitasi penyederhanaan API metadata tingkat rendah sehingga akses apa pun ke konsep metadata relasional umum selalu dilakukan melalui panggilan ke .Relational alih-alih .SqlServer, .Sqlite, dll.

Pengelogan dan diagnostik terkonsolidasi

Mekanisme pengelogan (berdasarkan ILogger) dan Diagnostik (berdasarkan DiagnosticSource) sekarang berbagi lebih banyak kode.

ID peristiwa untuk pesan yang dikirim ke ILogger telah berubah di 2.0. ID peristiwa sekarang unik di seluruh kode EF Core. Pesan tersebut sekarang juga mengikuti pola standar pengelogan terstruktur yang digunakan oleh, misalnya, MVC.

Kategori pencatat juga telah berubah. Kini ada serangkaian kategori terkenal yang diakses melalui DbLoggerCategory.

Peristiwa DiagnosticSource sekarang menggunakan nama ID peristiwa yang sama dengan pesan ILogger terkait.