Sdílet prostřednictvím


Nové funkce v EF Core 2.0

.NET Standard 2.0

EF Core nyní cílí na . NET Standard 2.0, což znamená, že dokáže pracovat s .NET Core 2.0, .NET Framework 4.6.1 a dalšími knihovnami, které implementují .NET Standard 2.0. Další podrobnosti o podporovaných implementacích .NET naleznete v tématu Podporované implementace .NET.

Modelování

Dělení tabulky

Nyní je možné namapovat dva nebo více typů entit na stejnou tabulku, kde budou sloupce primárního klíče sdílené a každý řádek bude odpovídat dvěma nebo více entitám.

Pokud chcete použít dělení tabulky, musí být mezi všemi typy entit sdílejícími tabulku nakonfigurována identifikující relace (kde primární klíč tvoří vlastnosti cizího klíče):

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");

Další informace o této funkci najdete v části věnované dělení tabulky.

Vlastněné typy

Vlastněný typ entity může sdílet stejný typ .NET s jiným vlastněným typem entity, ale vzhledem k tomu, že ho nelze identifikovat pouze pomocí typu .NET, musí na něj existovat navigace z jiného typu entity. Entita obsahující definující navigaci je vlastníkem. Při dotazování na vlastníka budou ve výchozím nastavení zahrnuty vlastněné typy.

Podle konvence bude pro vlastněný typ vytvořen stínový primární klíč, který bude namapován na stejnou tabulku jako vlastník, a to pomocí dělení tabulky. To umožňuje používat vlastněné typy podobným způsobem, jako se používají komplexní typy v 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; }
}

Další informace o této funkci najdete v části věnované vlastněným typům entit.

Filtry dotazů na úrovni modelu

EF Core 2.0 obsahuje novou funkci, kterou označujeme jako filtry dotazů na úrovni modelu. Tato funkce umožňuje definovat predikáty dotazu LINQ (logický výraz obvykle předávaný operátoru dotazu LINQ Where) přímo pro typy entit v modelu metadat (obvykle v OnModelCreating). Tyto filtry se automaticky použijí na všechny dotazy LINQ zahrnující tyto typy entit, včetně typů entit, na které se odkazuje nepřímo, například pomocí odkazů na vlastnost Include nebo pomocí přímých odkazů na navigační vlastnost. Mezi běžné způsoby použití této funkce patří:

  • Obnovitelné odstranění – Typy entit definují vlastnost IsDeleted.
  • Víceklientská architektura – Typ entity definuje vlastnost TenantId.

Zde je jednoduchý příklad demonstrující použití této funkce ve dvou výše uvedených scénářích:

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);
    }
}

Definujeme filtr na úrovni modelu, který implementuje víceklientskou architekturu a obnovitelné odstranění pro instance typu entity Post. Všimněte si použití vlastnosti na úrovni instance DbContext: TenantId. Filtry na úrovni modelu použijí hodnotu ze správné instance kontextu (tj. instance kontextu, která provádí dotaz).

Filtry lze pro jednotlivé dotazy LINQ zakázat pomocí operátoru IgnoreQueryFilters().

Omezení

  • Navigační odkazy nejsou povoleny. Tato funkce může být přidána na základě zpětné vazby.
  • Filtry lze definovat pouze pro kořenový typ entity hierarchie.

Mapování skalárních funkcí databáze

EF Core 2.0 obsahuje důležitý příspěvek od Paula Middletona, který umožňuje mapování skalárních funkcí databáze na zástupné metody, aby je bylo možné použít v dotazech LINQ a přeložit do jazyka SQL.

Tady je stručný popis způsobu použití funkce:

Deklarujte statickou metodu pro svůj DbContext a přidejte k ní poznámku pomocí DbFunctionAttribute:

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

Takovéto metody se registrují automaticky. Po registraci lze volání metody v dotazu LINQ převést na volání funkce v SQL:

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

Několik poznámek:

  • Podle konvence se při generování SQL použije název metody jako název funkce (v tomto případě uživatelsky definované funkce), ale při registraci metody můžete název a schéma přepsat.
  • Aktuálně jsou podporovány pouze skalární funkce.
  • Musíte vytvořit namapovanou funkci v databázi. Migrace EF Core její vytvoření nezajistí.

Konfigurace samostatného typu pro kód jako první

V EF6 bylo možné zapouzdřit první konfiguraci kódu konkrétního typu entity odvozením od EntityTypeConfiguration. V EF Core 2.0 se tento vzor vrací:

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());

High Performance

Sdružování DbContext

Základní vzor pro použití EF Core v aplikaci ASP.NET Core obvykle zahrnuje registraci vlastního typu DbContext do systému injektáže závislostí a následné získání instancí tohoto typu prostřednictvím parametrů konstruktoru v kontrolerech. To znamená, že pro každou žádost se vytvoří nová instance DbContext.

Ve verzi 2.0 zavádíme nový způsob registrace vlastních typů DbContext v rámci injektáže závislostí, který transparentně zavádí fond opakovaně použitelných instancí DbContext. Pokud chcete použít sdružování DbContext, použijte při registraci služby místo metody AddDbContext metodu AddDbContextPool:

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

Pokud je použita tato metoda, pak v okamžiku, kdy je instance DbContext požadována kontrolerem, nejprve zkontrolujeme, zda je ve fondu dostupná instance. Jakmile je zpracování žádosti dokončeno, je jakýkoli stav instance resetován a samotná instance je vrácena do fondu.

To je konceptuálně podobné tomu, jak funguje sdružování připojení u zprostředkovatelů ADO.NET, a má to výhodu v tom, že se ušetří část nákladů na inicializaci instance DbContext.

Omezení

Používání nové metody s sebou nese několik omezení ohledně toho, co lze v metodě OnConfiguring() typu DbContext provádět.

Upozorňující

Pokud ve své odvozené třídě DbContext udržujete vlastní stav (například privátní pole), který by neměl být sdílen napříč žádostmi, nepoužívejte sdružování DbContext. EF Core před přidáním instance DbContext do fondu resetuje pouze stav, o kterém ví.

Explicitně kompilované dotazy

Jedná se o druhou volitelnou (opt-in) funkci výkonu, která je navržena tak, aby nabízela výhody ve scénářích s vysokou škálovatelností.

V předchozích verzích EF a také v LINQ to SQL byla k dispozici rozhraní API pro ruční nebo explicitně kompilované dotazy, která aplikacím umožňovala ukládat překlad dotazů do mezipaměti, aby je stačilo vypočítat pouze jednou a mohly být provedeny vícekrát.

Přestože EF Core obecně dokáže automaticky kompilovat a ukládat do mezipaměti dotazy založené na hashované reprezentaci výrazů dotazu, lze tento mechanismus využít k dosažení malého nárůstu výkonu tím, že se obejde výpočet hodnoty hash a vyhledávání v mezipaměti, což aplikaci umožní použít již zkompilovaný dotaz prostřednictvím vyvolání delegáta.

// 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);
}

Sledování změn

Entita Attach může sledovat graf nových a existujících entit

EF Core podporuje automatické generování hodnot klíčů pomocí různých mechanismů. Při použití této funkce se vygeneruje hodnota, pokud je vlastností klíče výchozí hodnota CLR – obvykle nula nebo null. To znamená, že graf entit lze předat entitě DbContext.Attach nebo DbSet.Attach a EF Core označí ty entity, které již mají nastavený klíč, jako Unchanged, zatímco entity, které nemají nastavený klíč, budou označeny jako Added. To usnadňuje připojení grafu smíšených nových a existujících entit při použití vygenerovaných klíčů. Entity DbContext.Update a DbSet.Update fungují stejným způsobem s tím rozdílem, že entity s nastaveným klíčem nejsou označeny jako Unchanged, ale jako Modified.

Dotaz

Vylepšený překlad LINQ

Umožňuje úspěšně provést více dotazů, přičemž větší část logiky se vyhodnocuje v databázi (místo v paměti) a z databáze se zbytečně nenačítá tolik dat.

Vylepšení GroupJoin

Toto řešení vylepšuje kód SQL, který je generován pro spojování skupin. Spojení skupin je nejčastěji výsledkem dílčích dotazů na volitelné navigační vlastnosti.

Interpolace řetězců v FromSql a ExecuteSqlCommand

V C# 6 byla zavedena funkce interpolace řetězců, která umožňuje vkládat výrazy jazyka C# přímo do řetězcových literálů, což usnadňuje vytváření řetězců za běhu. V EF Core 2.0 jsme přidali speciální podporu pro interpolované řetězce do našich dvou hlavních rozhraní API, která přijímají nezpracované řetězce SQL: FromSql a ExecuteSqlCommand. Tato nová podpora umožňuje „bezpečné“ použití interpolace řetězců v jazyce C#. To znamená způsobem, který chrání před běžnými chybami injektáže SQL, k nimž může dojít při dynamickém sestavování kódu SQL za běhu.

Zde je příklad:

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();
  }

V tomto příkladu jsou do řetězce formátu SQL vloženy dvě proměnné. EF Core vytvoří následující kód SQL:

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

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

EF.Functions.Like()

Přidali jsme vlastnost EF.Functions, kterou můžou EF Core nebo zprostředkovatelé použít k definování metod, které se mapují na funkce nebo operátory databáze, aby je bylo možné vyvolat v dotazech LINQ. Prvním příkladem takové metody je Like():

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

Všimněte si, že metoda Like() je implementována v paměti, což se může hodit při práci s databází v paměti nebo v případě, že vyhodnocení predikátu musí proběhnout na straně klienta.

Správa databází

Pluralizační hook pro generování uživatelského rozhraní DbContext

EF Core 2.0 zavádí novou službu IPluralizer, která se používá k singularizaci názvů typů entit a pluralizaci názvů DbSet. Výchozí implementace je bez možnosti volby, takže se jedná pouze o hook, na který mohou uživatelé snadno připojit svůj vlastní pluralizátor.

Tady je ukázka toho, jak vypadá zapojení vlastního pluralizátoru z pohledu vývojáře:

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;
    }
}

Jiný

Přesunutí zprostředkovatele ADO.NET SQLite do SQLitePCL.raw

Díky tomu máme v Microsoft.Data.Sqlite robustnější řešení pro distribuci nativních binárních souborů SQLite na jiných platformách.

Pouze jeden zprostředkovatel na jeden model

Výrazně rozšiřuje možnosti interakce zprostředkovatelů s modelem a zjednodušuje fungování konvencí, poznámek a fluent API s různými zprostředkovateli.

EF Core 2.0 nyní pro každého použitého zprostředkovatele vytvoří jiný IModel. To je pro aplikaci obvykle transparentní. To usnadnilo zjednodušení rozhraní API pro metadata nižší úrovně tak, že jakýkoli přístup k běžným konceptům relačních metadat se vždy provádí prostřednictvím volání .Relational místo .SqlServer, .Sqlite atd.

Konsolidované protokolování a diagnostika

Mechanismy protokolování (založené na ILogger) a diagnostiky (založené na DiagnosticSource) nyní sdílejí více kódu.

ID událostí pro zprávy odesílané do rozhraní ILogger se ve verzi 2.0 změnila. ID událostí jsou nyní napříč kódem EF Core jedinečná. Tyto zprávy se nyní také řídí standardním vzorem pro strukturované protokolování, který používá například MVC.

Změnily se také kategorie protokolovacího nástroje. Nyní je k dispozici známá sada kategorií, ke kterým se přistupuje přes DbLoggerCategory.

Události DiagnosticSource nyní používají stejné názvy ID událostí jako odpovídající zprávy ILogger.