Nové funkce ve EF Core 2.0

.NET Standard 2.0

EF Core teď cílí na .NET Standard 2.0, což znamená, že můž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 najdete v tématu Podporované implementace .NET.

Modelování

Dělení tabulky

Teď je možné mapovat dva nebo více typů entit na stejnou tabulku, ve které se budou sdílet sloupce primárního klíče a každý řádek bude odpovídat dvěma nebo více entitám.

Pokud chcete použít tabulku rozdělující identifikující relaci (kde vlastnosti cizího klíče tvoří primární klíč), musí být nakonfigurované mezi všemi typy entit sdílející tabulku:

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 o rozdělení tabulek.

Vlastněné typy

Vlastněný typ entity může sdílet stejný typ .NET s jiným vlastněnou entitou, ale protože ho nelze identifikovat pouze typem .NET, musí na něj být navigace z jiného typu entity. Vlastníkem je entita obsahující definující navigaci. Při dotazování vlastníka se ve výchozím nastavení zahrnou vlastněné typy.

Podle konvence se pro vlastněný typ vytvoří stínový primární klíč a pomocí rozdělení tabulky se namapuje na stejnou tabulku jako vlastník. To umožňuje používat vlastněné typy podobně jako složité typy používané 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 o vlastněných typech entit.

Filtry dotazů na úrovni modelu

EF Core 2.0 obsahuje novou funkci, které říkáme Filtry dotazů na úrovni modelu. Tato funkce umožňuje definovat predikáty dotazů LINQ (logický výraz obvykle předaný operátoru dotazu LINQ Where) přímo na 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ě nepřímo odkazovaných typů entit, například pomocí odkazů na vlastnosti Include nebo přímé navigace. Mezi běžné aplikace této funkce, které se běžně vztahují, jsou:

  • Softwarové odstranění – Typy entit definují vlastnost IsDeleted.
  • Více tenantů – Typ entity definuje vlastnost TenantId.

Tady je jednoduchý příklad předvedení funkce pro dva výše uvedené scénáře:

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ícevrstvou a soft-delete pro instance Post typu entity. Všimněte si použití DbContext vlastnosti na úrovni instance: TenantId . Filtry na úrovni modelu budou používat hodnotu ze správné instance kontextu (to znamená instance kontextu, která spouští dotaz).

Filtry mohou být zakázány pro jednotlivé dotazy LINQ pomocí operátoru IgnoreQueryFilters().

Omezení

  • Odkazy na navigaci nejsou povolené. Tuto funkci je možné přidat na základě zpětné vazby.
  • Filtry lze definovat pouze u kořenového typu entity hierarchie.

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

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

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

Deklarujte statickou metodu a DbContext anotujte ji pomocí DbFunctionAttribute :

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

Metody, jako je tato, se automaticky zaregistrovat. Po registraci lze volání metody v dotazu LINQ přeložit na volání funkcí v SQL:

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

Pár věcí, které stojí za zmínku:

  • Podle konvence se název metody používá jako název funkce (v tomto případě uživatelem definovaná funkce) při generování SQL, ale název a schéma můžete přepsat během registrace metody.
  • V současné době se podporují pouze skalární funkce.
  • V databázi musíte vytvořit mapovanou funkci. EF Core migrace se o vytvoření nepostarají.

Nejprve samostatná konfigurace typu pro kód

V EF6 bylo možné zapouzdřit kód první konfiguraci konkrétního typu entity odvozením z EntityTypeConfiguration. Ve EF Core 2.0 tento model vracíme:

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 pozdější získání instancí tohoto typu prostřednictvím parametrů konstruktoru v kontrolerích. To znamená, že se pro každý požadavek vytvoří nová instance DbContext.

Ve verzi 2.0 představujeme nový způsob registrace vlastních typů DbContext v injektáži závislostí, který transparentně zavádí fond znovu použitelných instancí DbContext. Pokud chcete použít sdružování DbContext, použijte při registraci služby místo AddDbContextPoolAddDbContext parametru :

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

Pokud se používá tato metoda, v době, kdy kontroler požaduje instanci DbContext, nejprve zkontrolujeme, jestli je ve fondu dostupná instance. Jakmile se zpracování požadavku finalizuje, jakýkoli stav instance se resetuje a instance se vrátí do fondu.

To se koncepčně podobá fungování sdružování připojení ve zprostředkovatelích ADO.NET a má výhodu v tom, že ušetříte některé náklady na inicializaci instance DbContext.

Omezení

Nová metoda zavádí několik omezení, co je možné provést v OnConfiguring() metodě DbContext.

Upozornění

Vyhněte se používání sdružování DbContext, pokud si udržujete vlastní stav (například privátní pole) v odvozené třídě DbContext, která by se neměla sdílet mezi požadavky. EF Core instanci DbContext do fondu obnoví pouze stav, o němž ví.

Explicitně kompilované dotazy

Toto je druhá funkce pro výslovný souhlas s výkonem navržená tak, aby nabídla výhody ve scénářích ve velkém měřítku.

Rozhraní API ručních nebo explicitně kompilovaných dotazů byla k dispozici v předchozích verzích EF a také v systému LINQ to SQL, aby aplikace mohly ukládat překlad dotazů do mezipaměti, aby je bylo možné vypočítat pouze jednou a provést mnohokrát.

Přestože obecně platí, že EF Core může automaticky kompilovat a ukládat dotazy do mezipaměti na základě hashované reprezentace výrazů dotazu, lze tento mechanismus použít k získání malého zvýšení výkonu obejitím výpočtu hodnoty hash a vyhledávání v mezipaměti, což aplikaci umožní používat 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

Připojení může sledovat graf nových a existujících entit.

EF Core podporuje automatické generování hodnot klíčů prostřednictvím různých mechanismů. Při použití této funkce se vygeneruje hodnota, pokud je vlastnost key výchozí hodnotou CLR – obvykle nulová nebo null. To znamená, že graf entit lze předat do nebo a EF Core označí entity, které už mají klíč nastavený jako , zatímco entity, které nemají sadu klíčů, budou označené DbContext.AttachDbSet.Attach jako UnchangedAdded . To usnadňuje připojení grafu smíšených nových a existujících entit při použití generovaných klíčů. DbContext.UpdateDbSet.Updatea fungují stejným způsobem, s tím rozdílem, že entity se sadu klíčů jsou označené jako místo ModifiedUnchanged .

Dotaz

Vylepšený překlad LINQ

Umožňuje úspěšné spuštění více dotazů, kdy se v databázi (místo v paměti) vyhodnocuje větší logika a z databáze se zbytečně načítá méně dat.

Vylepšení groupjoin

Tato práce vylepšuje SQL, která se generuje pro spojení skupin. Spojení skupin jsou nejčastěji výsledkem dílčích dotazů u volitelných navigačních vlastností.

Interpolace řetězců v FromSql a ExecuteSqlCommand

Jazyk C# 6 zavedl interpolaci řetězců, což je funkce, která umožňuje, aby výrazy jazyka C# byly přímo vloženy do řetězcových literálů, což poskytuje skvělý způsob vytváření řetězců za běhu. Ve EF Core 2.0 jsme do našich dvou primárních rozhraní API, která přijímají nezpracované řetězce SQL, přidali zvláštní podporu interpolovaných řetězců: a FromSqlExecuteSqlCommand . Tato nová podpora umožňuje "bezpečnou" interpolaci řetězců v jazyce C#. To znamená způsobem, který chrání před běžnými chybami SQL injektáží, ke kterým může dojít při dynamickém SQL za běhu.

Zde naleznete 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 v řetězci formátu SQL dvě proměnné. EF Core vytvoří následující 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 EF. Vlastnost Functions, kterou zprostředkovatelé mohou EF Core nebo použít k definování metod, které se mapují na databázové funkce nebo operátory, 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 like() se dodává s implementací v paměti, která se může pohoutit při práci s databází v paměti nebo při vyhodnocení predikátu na straně klienta.

Správa databází

Hook pluralizace 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 no-op, takže se jedná jen o hook, kde se lidé mohou snadno připojit k vlastnímu pluralizéru.

Tady je, jak to vypadá, když vývojář používá vlastní pluralizér:

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

Ostatní

Přesun ADO.NET SQLite do SQLitePCL.raw

V Microsoft.Data.Sqlite tak získáte robustnější řešení pro distribuci nativních binárních souborů SQLite na různých platformách.

Pouze jeden poskytovatel na model

Významně rozšiřuje způsob, jakým můžou poskytovatelé pracovat s modelem a zjednodušuje, jak konvence, poznámky a rozhraní API Fluent pracují s různými poskytovateli.

EF Core 2,0 teď pro každého jiného zprostředkovatele vytvoří jiný IModel . To je obvykle transparentní pro aplikaci. Usnadnili jsme tak zjednodušení rozhraní API na nižší úrovni tak, aby jakýkoliv přístup k běžným koncepcím relačních metadat byl vždy proveden prostřednictvím volání namísto .SqlServer , .Sqlite atd.

Konsolidovaná protokolování a diagnostika

Protokolování (založené na ILogger) a diagnostické mechanismy (založené na DiagnosticSource) nyní sdílí více kódů.

ID událostí pro zprávy odeslané do ILogger se změnila v 2,0. Identifikátory událostí jsou teď v rámci EF Core kódu jedinečné. Tyto zprávy se teď také řídí standardním vzorem pro strukturované protokolování, které používá, například MVC.

Změnily se také kategorie protokolovacího nástroje. K dispozici je teď známá sada kategorií, ke které se dostanete prostřednictvím DbLoggerCategory.

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