Nouvelles fonctionnalités d’EF Core 2.0New features in EF Core 2.0

.NET Standard 2.0.NET Standard 2.0

EF Core cible désormais .NET Standard 2.0, ce qui signifie qu’il peut fonctionner avec .NET Core 2.0, .NET Framework 4.6.1 et d’autres bibliothèques qui implémentent .NET Standard 2.0.EF Core now targets .NET Standard 2.0, which means it can work with .NET Core 2.0, .NET Framework 4.6.1, and other libraries that implement .NET Standard 2.0. Pour plus d’informations sur ce qui est pris en charge, consultez Implémentations .NET prises en charge.See Supported .NET Implementations for more details on what is supported.

ModélisationModeling

Fractionnement de tableTable splitting

Vous pouvez désormais mapper deux ou plusieurs types d’entité à la même table, où la ou les colonnes de clé primaire sont partagées et chaque ligne correspond à deux ou plusieurs entités.It is now possible to map two or more entity types to the same table where the primary key column(s) will be shared and each row will correspond to two or more entities.

Pour utiliser le fractionnement de table, vous devez configurer une relation d’identification (où les propriétés de clé étrangère forment la clé primaire) entre tous les types d’entité partageant la table :To use table splitting an identifying relationship (where foreign key properties form the primary key) must be configured between all of the entity types sharing the table:

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

Types détenusOwned types

Un type d’entité détenu peut partager le même type CLR avec un autre type d’entité détenu, mais dans la mesure où il ne peut pas être identifié par le seul type CLR, un dispositif de navigation est nécessaire pour y accéder à partir d’un autre type d’entité.An owned entity type can share the same CLR type with another owned entity type, but since it cannot be identified just by the CLR type there must be a navigation to it from another entity type. L’entité qui contient la navigation définie est le propriétaire.The entity containing the defining navigation is the owner. Quand le propriétaire fait l’objet d’une interrogation, les types détenus sont inclus par défaut.When querying the owner the owned types will be included by default.

Par convention, une clé primaire cachée est créée pour le type détenu et est mappée à la même table que le propriétaire à l’aide du fractionnement de table.By convention a shadow primary key will be created for the owned type and it will be mapped to the same table as the owner by using table splitting. Cela permet d’utiliser des types détenus à l’image des types complexes dans EF6 :This allows to use owned types similarly to how complex types are used in 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; }
}

Pour plus d’informations sur cette fonctionnalité, lisez la section sur les types d’entités détenus.Read the section on owned entity types for more information on this feature.

Filtres de requête au niveau du modèleModel-level query filters

EF Core 2.0 inclut une nouvelle fonctionnalité que nous appelons « filtres de requête au niveau du modèle ».EF Core 2.0 includes a new feature we call Model-level query filters. Cette fonctionnalité permet aux prédicats de requête LINQ (expression booléenne généralement passée à l’opérateur de requête LINQ Where) d’être définis directement sur des types d’entité dans le modèle de métadonnées (généralement dans OnModelCreating).This feature allows LINQ query predicates (a boolean expression typically passed to the LINQ Where query operator) to be defined directly on Entity Types in the metadata model (usually in OnModelCreating). Ces filtres sont automatiquement appliqués à toutes les requêtes LINQ impliquant ces types d’entités, y compris ceux référencés indirectement, par exemple à l’aide d’Include ou de références de propriété de navigation directe.Such filters are automatically applied to any LINQ queries involving those Entity Types, including Entity Types referenced indirectly, such as through the use of Include or direct navigation property references. Voici deux applications courantes de cette fonctionnalité :Some common applications of this feature are:

  • Suppression réversible : un type d’entité définit une propriété IsDeleted.Soft delete - An Entity Types defines an IsDeleted property.
  • Architecture multilocataire : un type d’entité définit une propriété TenantId.Multi-tenancy - An Entity Type defines a TenantId property.

Voici un exemple simple illustrant la fonctionnalité pour les deux scénarios répertoriés ci-dessus :Here is a simple example demonstrating the feature for the two scenarios listed above:

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

Nous définissons un filtre au niveau du modèle qui implémente une architecture multilocataire et une suppression réversible pour des instances du type d’entité Post.We define a model-level filter that implements multi-tenancy and soft-delete for instances of the Post Entity Type. Notez l’utilisation d’une propriété de niveau instance DbContext : TenantId.Note the use of a DbContext instance level property: TenantId. Les filtres au niveau du modèle utilisent la valeur de l’instance de contexte correcte,Model-level filters will use the value from the correct context instance. c’est-à-dire, celle qui exécute la requête.I.e. the one that is executing the query.

Les filtres peuvent être désactivés pour des requêtes LINQ individuelles à l’aide de l’opérateur IgnoreQueryFilters().Filters may be disabled for individual LINQ queries using the IgnoreQueryFilters() operator.

LimitationsLimitations

  • Les références de navigation ne sont pas autorisées.Navigation references are not allowed. Cette fonctionnalité peut être ajoutée en fonction de vos commentaires.This feature may be added based on feedback.
  • Les filtres ne peuvent être définis que sur le type d’entité racine d’une hiérarchie.Filters can only be defined on the root Entity Type of a hierarchy.

Mappage de fonctions scalaires de base de donnéesDatabase scalar function mapping

EF Core 2.0 inclut une contribution importante de Paul Middleton qui permet de mapper les fonctions scalaires de base de données à des stubs de méthode en vue de les utiliser dans des requêtes LINQ et de les convertir en SQL.EF Core 2.0 includes an important contribution from Paul Middleton which enables mapping database scalar functions to method stubs so that they can be used in LINQ queries and translated to SQL.

Voici une brève description de l’utilisation de la fonctionnalité :Here is a brief description of how the feature can be used:

Déclarez une méthode statique sur votre DbContext et annotez-la avec DbFunctionAttribute :Declare a static method on your DbContext and annotate it with DbFunctionAttribute:

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

Les méthodes telles que celle-ci sont inscrites automatiquement.Methods like this are automatically registered. Une fois inscrits, les appels à la méthode dans une requête LINQ peuvent être traduits en appels de fonction dans SQL :Once registered, calls to the method in a LINQ query can be translated to function calls in SQL:

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

Quelques points à noter :A few things to note:

  • Par convention, le nom de la méthode est utilisé comme nom de fonction (dans ce cas, une fonction définie par l’utilisateur) au moment de la génération du code SQL, mais vous pouvez remplacer le nom et le schéma durant l’inscription de la méthode.By convention the name of the method is used as the name of a function (in this case a user defined function) when generating the SQL, but you can override the name and schema during method registration
  • Seules les fonctions scalaires sont prises en charge.Currently only scalar functions are supported
  • Vous devez créer la fonction mappée dans la base de données ; par exemple, EF Core Migrations ne se charge pas de cette création.You must create the mapped function in the database, e.g. EF Core migrations will not take care of creating it

Configuration du type autonome pour Code FirstSelf-contained type configuration for code first

Dans EF6, il était possible d’encapsuler la configuration Code First d’un type d’entité spécifique en effectuant une dérivation à partir d’EntityTypeConfiguration.In EF6 it was possible to encapsulate the code first configuration of a specific entity type by deriving from EntityTypeConfiguration. Dans EF Core 2.0, nous reprenons ce modèle :In EF Core 2.0 we are bringing this pattern back:

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

Performances élevéesHigh Performance

Regroupement DbContextDbContext pooling

En règle générale, le modèle de base pour l’utilisation d’EF Core dans une application ASP.NET Core implique l’inscription d’un type DbContext personnalisé dans le système d’injection de dépendances, puis l’obtention d’instances de ce type par le biais de paramètres de constructeur dans les contrôleurs.The basic pattern for using EF Core in an ASP.NET Core application usually involves registering a custom DbContext type into the dependency injection system and later obtaining instances of that type through constructor parameters in controllers. Cela signifie qu’une instance de DbContext est créée pour chaque demande.This means a new instance of the DbContext is created for each requests.

La version 2.0 offre une nouvelle façon d’inscrire des types DbContext personnalisés dans l’injection de dépendances, qui introduit en toute transparence un groupe d’instances de DbContext réutilisables.In version 2.0 we are introducing a new way to register custom DbContext types in dependency injection which transparently introduces a pool of reusable DbContext instances. Pour utiliser le regroupement DbContext, utilisez AddDbContextPool au lieu d’AddDbContext durant l’inscription des services :To use DbContext pooling, use the AddDbContextPool instead of AddDbContext during service registration:

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

Si cette méthode est utilisée, au moment où une instance de DbContext est demandée par un contrôleur, nous vérifions d’abord s’il existe une instance disponible dans le pool.If this method is used, at the time a DbContext instance is requested by a controller we will first check if there is an instance available in the pool. Une fois finalisé le traitement de la demande, tout état sur l’instance est réinitialisé et l’instance elle-même est retournée au pool.Once the request processing finalizes, any state on the instance is reset and the instance is itself returned to the pool.

Ce regroupement est conceptuellement semblable au regroupement de connexions dans les fournisseurs ADO.NET et présente l’avantage de réduire les coûts d’initialisation de l’instance de DbContext.This is conceptually similar to how connection pooling operates in ADO.NET providers and has the advantage of saving some of the cost of initialization of DbContext instance.

LimitationsLimitations

La nouvelle méthode présente quelques limitations quant à ce qui peut être effectué dans la méthode OnConfiguring() de DbContext.The new method introduces a few limitations on what can be done in the OnConfiguring() method of the DbContext.

Avertissement

Évitez d’utiliser le regroupement DbContext si vous gérez votre propre état (par exemple, champs privés) dans votre classe DbContext dérivée qui ne doit pas être partagée entre les demandes.Avoid using DbContext Pooling if you maintain your own state (e.g. private fields) in your derived DbContext class that should not be shared across requests. EF Core réinitialise uniquement l’état dont il a connaissance avant d’ajouter une instance de DbContext au pool.EF Core will only reset the state that is aware of before adding a DbContext instance to the pool.

Requêtes compilées explicitementExplicitly compiled queries

Deux fonctionnalités de gain de performance à utiliser au choix (opt-in) ont été conçues pour offrir des avantages dans les scénarios à grande échelle.This is the second opt-in performance features designed to offer benefits in high-scale scenarios.

Les API de requête manuelles ou compilées explicitement étaient disponibles dans les versions précédentes d’EF, ainsi que dans LINQ to SQL, pour permettre aux applications de mettre en cache la conversion des requêtes afin qu’elles puissent être calculées une seule fois et exécutées plusieurs fois.Manual or explicitly compiled query APIs have been available in previous versions of EF and also in LINQ to SQL to allow applications to cache the translation of queries so that they can be computed only once and executed many times.

Bien qu’en général EF Core puisse automatiquement compiler et mettre en cache les requêtes en fonction d’une représentation hachée des expressions de requête, ce mécanisme peut être utilisé pour obtenir un petit gain de performances en contournant le calcul du hachage et la recherche dans le cache, ce qui permet à l’application d’utiliser une requête déjà compilée par le biais de l’appel d’un délégué.Although in general EF Core can automatically compile and cache queries based on a hashed representation of the query expressions, this mechanism can be used to obtain a small performance gain by bypassing the computation of the hash and the cache lookup, allowing the application to use an already compiled query through the invocation of a delegate.

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

Suivi des modificationsChange Tracking

Attach peut suivre un graphique des entités nouvelles et existantesAttach can track a graph of new and existing entities

EF Core prend en charge la génération automatique des valeurs de clés par le biais d’une variété de mécanismes.EF Core supports automatic generation of key values through a variety of mechanisms. Quand vous utilisez cette fonctionnalité, une valeur est générée si la propriété de clé est la valeur par défaut du CLR, généralement zéro ou null.When using this feature, a value is generated if the key property is the CLR default--usually zero or null. Cela signifie qu’un graphique d’entités peut être transmis à DbContext.Attach ou DbSet.Attach et qu’EF Core marque les entités qui ont déjà une clé définie comme Unchanged tandis que les entités qui n’ont pas de clé définie sont marquées comme Added.This means that a graph of entities can be passed to DbContext.Attach or DbSet.Attach and EF Core will mark those entities that have a key already set as Unchanged while those entities that do not have a key set will be marked as Added. Il est ainsi plus facile de joindre un graphique regroupant des entités nouvelles et existantes quand des clés générées sont utilisées.This makes it easy to attach a graph of mixed new and existing entities when using generated keys. DbContext.Update et DbSet.Update fonctionnent de la même manière, à la différence que les entités ayant une clé définie sont marquées comme Modified au lieu de Unchanged.DbContext.Update and DbSet.Update work in the same way, except that entities with a key set are marked as Modified instead of Unchanged.

QueryQuery

Conversion LINQ amélioréeImproved LINQ Translation

Permet l’exécution de davantage de requêtes, avec davantage de logique évaluée dans la base de données (plutôt qu’en mémoire) et moins de données inutilement récupérées de la base de données.Enables more queries to successfully execute, with more logic being evaluated in the database (rather than in-memory) and less data unnecessarily being retrieved from the database.

Améliorations apportées aux jonctions de groupesGroupJoin improvements

Le SQL généré pour les jonctions de groupes est amélioré.This work improves the SQL that is generated for group joins. Les jonctions de groupes sont souvent le résultat de sous-requêtes sur des propriétés de navigation facultatives.Group joins are most often a result of sub-queries on optional navigation properties.

Interpolation de chaîne dans FromSql et ExecuteSqlCommandString interpolation in FromSql and ExecuteSqlCommand

C# 6 a introduit l’interpolation de chaîne, fonctionnalité qui permet d’incorporer les expressions C# directement dans des littéraux de chaîne, offrant ainsi un moyen agréable de générer des chaînes au moment de l’exécution.C# 6 introduced String Interpolation, a feature that allows C# expressions to be directly embedded in string literals, providing a nice way of building strings at runtime. Dans EF Core 2.0, nous avons ajouté une prise en charge spéciale pour les chaînes interpolées à nos deux API principales qui acceptent des chaînes SQL brutes : FromSql et ExecuteSqlCommand.In EF Core 2.0 we added special support for interpolated strings to our two primary APIs that accept raw SQL strings: FromSql and ExecuteSqlCommand. Avec cette nouvelle prise en charge, l’interpolation de chaîne C# peut être utilisée de manière « sécurisée »,This new support allows C# string interpolation to be used in a 'safe' manner. c’est-à-dire, d’une façon qui protège contre les erreurs d’injection SQL courantes pouvant se produire durant la construction dynamique du code SQL au moment de l’exécution.I.e. in a way that protects against common SQL injection mistakes that can occur when dynamically constructing SQL at runtime.

Voici un exemple :Here is an example:

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

Dans cet exemple, il existe deux variables incorporées dans la chaîne de format SQL.In this example, there are two variables embedded in the SQL format string. EF Core génère le code SQL suivant :EF Core will produce the following SQL:

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

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

EF.Functions.Like()EF.Functions.Like()

Nous avons ajouté la propriété EF.Functions qui peut être utilisée par EF Core ou des fournisseurs pour définir des méthodes mappées à des fonctions ou des opérateurs de base de données afin qu’elles puissent être appelées dans les requêtes LINQ.We have added the EF.Functions property which can be used by EF Core or providers to define methods that map to database functions or operators so that those can be invoked in LINQ queries. Le premier exemple de ce type de méthode est Like() :The first example of such a method is Like():

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

Notez que Like() est fourni avec une implémentation en mémoire, ce qui peut être pratique quand vous utilisez une base de données en mémoire ou que l’évaluation du prédicat doit se produire côté client.Note that Like() comes with an in-memory implementation, which can be handy when working against an in-memory database or when evaluation of the predicate needs to occur on the client side.

Gestion de base de donnéesDatabase management

Dispositif de pluralisation pour structurer un DbContextPluralization hook for DbContext Scaffolding

EF Core 2.0 introduit un nouveau service IPluralizer qui est utilisé pour singulariser les noms des types d’entité et pluraliser les noms DbSet.EF Core 2.0 introduces a new IPluralizer service that is used to singularize entity type names and pluralize DbSet names. L’implémentation par défaut étant l’absence d’opération, il s’agit simplement d’un dispositif permettant à l’utilisateur d’incorporer facilement son propre pluraliseur.The default implementation is a no-op, so this is just a hook where folks can easily plug in their own pluralizer.

Voici à quoi cela ressemble dans le cas d’un développeur souhaitant mettre en place son propre pluraliseur :Here is what it looks like for a developer to hook in their own pluralizer:

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

AutresOthers

Déplacement du fournisseur ADO.NET SQLite vers SQLitePCL.rawMove ADO.NET SQLite provider to SQLitePCL.raw

Nous disposons ainsi d’une solution plus fiable dans Microsoft.Data.Sqlite pour distribuer des fichiers binaires SQLite natifs sur différentes plateformes.This gives us a more robust solution in Microsoft.Data.Sqlite for distributing native SQLite binaries on different platforms.

Un seul fournisseur par modèleOnly one provider per model

Renforce considérablement les possibilités d’interaction des fournisseurs avec le modèle et simplifie le fonctionnement des conventions, annotations et API Fluent avec les différents fournisseurs.Significantly augments how providers can interact with the model and simplifies how conventions, annotations and fluent APIs work with different providers.

EF Core 2.0 génère désormais un IModel différent par fournisseur utilisé.EF Core 2.0 will now build a different IModel for each different provider being used. Cela est généralement transparent pour l’application.This is usually transparent to the application. Il en résulte une simplification des API de métadonnées de niveau inférieur, au point que tout accès aux concepts de métadonnées relationnelles communs est toujours établi par le biais d’un appel à .Relational au lieu de .SqlServer, .Sqlite, etc.This has facilitated a simplification of lower-level metadata APIs such that any access to common relational metadata concepts is always made through a call to .Relational instead of .SqlServer, .Sqlite, etc.

Journalisation consolidée et diagnosticsConsolidated Logging and Diagnostics

Les mécanismes de journalisation (basés sur ILogger) et de diagnostics (basés sur DiagnosticSource) partagent désormais plus de code.Logging (based on ILogger) and Diagnostics (based on DiagnosticSource) mechanisms now share more code.

Les ID d’événement pour les messages envoyés à un ILogger ont été changés dans la version 2.0.The event IDs for messages sent to an ILogger have changed in 2.0. Les ID d’événement sont maintenant uniques dans le code EF Core.The event IDs are now unique across EF Core code. En outre, ces messages suivent désormais le modèle standard de la journalisation structurée utilisé, par exemple, par le modèle MVC.These messages now also follow the standard pattern for structured logging used by, for example, MVC.

Les catégories d’enregistreurs d’événements ont également changé.Logger categories have also changed. Il existe désormais un jeu connu de catégories accessibles par le biais de DbLoggerCategory.There is now a well-known set of categories accessed through DbLoggerCategory.

Les événements DiagnosticSource utilisent désormais les mêmes noms d’ID d’événement que les messages ILogger correspondants.DiagnosticSource events now use the same event ID names as the corresponding ILogger messages.