EF Core 2.0 の新機能New features in EF Core 2.0

.NET Standard 2.0.NET Standard 2.0

EF Core が .NET Standard 2.0 対応になりました。つまり、.NET Core 2.0、.NET Framework 4.6.1、および .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. サポート対象の詳細については、「Supported .NET Implementations」 (サポートされている .NET 実装) を参照してください。See Supported .NET Implementations for more details on what is supported.

モデリングModeling

テーブル分割Table splitting

2 つまたはそれ以上のエンティティ型を同じテーブルにマッピングできるようになりました。そこで主キー列が共有され、各行が 2 つまたはそれ以上のエンティティに対応します。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.

テーブル分割を利用するには、テーブルを共有するすべてのエンティティ型の間で依存リレーションシップ (外部キー プロパティが主キーを形成する) を構成する必要があります。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");

この機能の詳細については、テーブル分割に関するページを参照してください。Read the section on table splitting for more information on this feature.

所有されている型Owned types

所有されているエンティティ型は、別の所有されているエンティティ型と同じ .NET 型を共有できます。ただし、.NET 型だけでは識別できないため、別のエンティティ型からそれへのナビゲーションが必要になります。An owned entity type can share the same .NET type with another owned entity type, but since it cannot be identified just by the .NET type there must be a navigation to it from another entity type. 定義となるナビゲーションを含むエンティティは所有者です。The entity containing the defining navigation is the owner. 所有者に問い合わせるとき、所有されている型が既定で含まれます。When querying the owner the owned types will be included by default.

慣例によって、所有されている型に対してシャドウ主キーが作成され、テーブル分割を利用し、同じテーブルに所有者としてマッピングされます。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. それによって、所有されている型を 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; }
}

この機能に関する詳細は、所有エンティティ型のセクションをご覧ください。Read the section on owned entity types for more information on this feature.

モデルレベルのクエリ フィルターModel-level query filters

EF Core 2.0 には、モデルレベルのクエリ フィルターと呼んでいる新しい機能があります。EF Core 2.0 includes a new feature we call Model-level query filters. この機能では、(通常 OnModelCreating の) メタデータ モデルのエンティティ型で直接、LINQ クエリ述語 (通常 LINQ Where クエリ演算子に渡されるブール式) を定義できます。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). このようなフィルターは、このようなエンティティ型が関係するあらゆる LINQ クエリに自動的に適用されます。これには間接的に参照されるエンティティ型が含まれます。たとえば、Include を利用して参照されます。あるいは、ナビゲーション プロパティが直接参照されます。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. この機能の一般的な用途は次のようになっています。Some common applications of this feature are:

  • 論理削除 - エンティティ型が IsDeleted プロパティを定義します。Soft delete - An Entity Types defines an IsDeleted property.
  • マルチテナント - エンティティ型が TenantId プロパティを定義します。Multi-tenancy - An Entity Type defines a TenantId property.

次の単純なサンプルでは、上記の 2 つのシナリオに対してこの機能が使われています。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);
    }
}

エンティティ型 Post のインスタンスに対してマルチテナントと論理削除を実行するモデルレベル フィルターを定義します。We define a model-level filter that implements multi-tenancy and soft-delete for instances of the Post Entity Type. DbContext インスタンス レベル プロパティ TenantId の使用に注目してください。Note the use of a DbContext instance-level property: TenantId. モデルレベル フィルターは、正しいコンテキスト インスタンス (つまり、クエリを実行しているコンテキスト インスタンス) の値を使用します。Model-level filters will use the value from the correct context instance (that is, the context instance that is executing the query).

フィルターは、IgnoreQueryFilters() 演算子を利用し、個々の LINQ クエリに対して無効にできます。Filters may be disabled for individual LINQ queries using the IgnoreQueryFilters() operator.

制限事項Limitations

  • ナビゲーション参照は許可されていません。Navigation references are not allowed. この機能はフィードバックに応じて追加される可能性があります。This feature may be added based on feedback.
  • フィルターは、階層のエンティティ型ルートでのみ定義できます。Filters can only be defined on the root Entity Type of a hierarchy.

データベース スカラー関数マッピングDatabase scalar function mapping

EF Core 2.0 には、Paul Middleton の重要な貢献が不可欠でした。それは、データベース スカラー関数のメソッド スタブへのマッピングを可能にするものです。マッピングにより、LINQ クエリで使用したり、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.

この機能の使用方法を簡単に説明すると次のようになります。Here is a brief description of how the feature can be used:

DbContext で静的メソッドを宣言し、それに 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 NotImplementedException();
    }
}

このようなメソッドが自動的に登録されます。Methods like this are automatically registered. 登録されると、LINQ クエリでのメソッドへの呼び出しが 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;

次のことに注意してください。A few things to note:

  • 規則では、SQL の生成時、関数 (この場合はユーザー定義関数) の名前としてメソッドの名前が使用されますが、メソッド登録時に名前とスキーマをオーバーライドできます。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.
  • 現在のところ、スカラー関数のみがサポートされています。Currently only scalar functions are supported.
  • データベースにマップされた関数を作成する必要があります。You must create the mapped function in the database. EF Core の移行では作成が行われません。EF Core migrations will not take care of creating it.

Code First の自己完結型構成Self-contained type configuration for code first

EF6 では、EntityTypeConfiguration から派生することで、特定のエンティティ型の Code First 構成をカプセル化できました。In EF6 it was possible to encapsulate the code first configuration of a specific entity type by deriving from EntityTypeConfiguration. EF Core 2.0 では、このパターンが復活します。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());

高パフォーマンスHigh Performance

DbContext プールDbContext pooling

ASP.NET Core アプリケーションで EF Core を使用する基本パターンは通常、カスタム DbContext 型を依存関係挿入システムに登録し、その後、コントローラーのコンストラクター パラメーターからその型のインスタンスを取得するというパターンになります。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. つまり、DbContext の新しいインスタンスが要求ごとに作成されます。This means a new instance of the DbContext is created for each request.

バージョン 2.0 では、依存関係挿入でカスタム DbContext 型を登録する新しい方法を導入しました。再利用可能な DbContext インスタンスのプールが透過的に導入されます。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. DbContext プールを使用するには、サービス登録で AddDbContext の代わりに AddDbContextPool を使用します。To use DbContext pooling, use the AddDbContextPool instead of AddDbContext during service registration:

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

この方法を利用する場合、コントローラーが DbContext インスタンスを要求した時点で、プールに利用できるインスタンスがないか最初に確認されます。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. 要求処理が終わると、インスタンス上のあらゆる状態がリセットされ、インスタンス自体はプールに戻ります。Once the request processing finalizes, any state on the instance is reset and the instance is itself returned to the pool.

これは概念としては、DbContext プロバイダーの接続プールと似ており、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.

制限事項Limitations

この新しい方法では、DbContext の OnConfiguring() メソッドでできることにいくつかの制限があります。The new method introduces a few limitations on what can be done in the OnConfiguring() method of the DbContext.

警告

派生した DbContext クラス (要求間で共有できない) で独自の状態を維持する場合 (プライベート フィールドなど)、DbContext プールを使用しないでください。Avoid using DbContext Pooling if you maintain your own state (for example, private fields) in your derived DbContext class that should not be shared across requests. EF Core は、DbContext インスタンスをプールに追加する前に認識した状態のみをリセットします。EF Core will only reset the state that it is aware of before adding a DbContext instance to the pool.

明示的にコンパイルされたクエリExplicitly compiled queries

これは 2 つ目のオプトイン パフォーマンス機能であり、大規模なシナリオで利点が得られるように設計されています。This is the second opt-in performance feature designed to offer benefits in high-scale scenarios.

以前のバージョンの EF や LINQ to SQL では、手動または明示的にコンパイルされたクエリ API を利用できました。アプリケーションでは、1 回だけ計算し、何回も実行できるよう、クエリの変換をキャッシュできました。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.

一般的に、EF Core は、ハッシュ後のクエリ式に基づいてクエリを自動的にコンパイルし、キャッシュできますが、このメカニズムを利用し、ハッシュとキャッシュ参照の計算をバイパスすることでパフォーマンスを少し上げることができます。アプリケーションは、デリゲートを呼び出すことで、コンパイル済みのクエリを利用できます。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);
}

変更追跡Change Tracking

Attach で新規および既存のエンティティのグラフを追跡可能Attach can track a graph of new and existing entities

EF Core では、さまざまメカニズムを利用し、キー値を自動生成できます。EF Core supports automatic generation of key values through a variety of mechanisms. この機能を使用するとき、キー プロパティが CLR 既定の場合 (通常はゼロか null)、値が生成されます。When using this feature, a value is generated if the key property is the CLR default--usually zero or null. つまり、エンティティのグラフを DbContext.Attach または DbSet.Attach に渡すことができます。EF Core は、既にキーが設定されているエンティティを Unchanged としてマークします。キーが設定されていないエンティティは 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. それにより、生成されたキーを使用するとき、新規と既存の混合エンティティのグラフを簡単に添付できるようになります。This makes it easy to attach a graph of mixed new and existing entities when using generated keys. DbContext.UpdateDbSet.Update は同じように動作しますが、キーが設定されているエンティティは Unchanged ではなく Modified としてマークされます。DbContext.Update and DbSet.Update work in the same way, except that entities with a key set are marked as Modified instead of Unchanged.

クエリQuery

強化された LINQ 変換Improved LINQ translation

より多くのクエリを正常に実行できるようになりました。(メモリではなく) データベースで評価されるロジックが増え、データベースから取得できないデータが減りました。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.

GroupJoin の改善GroupJoin improvements

これにより、グループ結合に対して生成される SQL が改善されます。This work improves the SQL that is generated for group joins. グループ結合は、大抵の場合、オプションのナビゲーション プロパティでのサブクエリの結果です。Group joins are most often a result of sub-queries on optional navigation properties.

FromSql と ExecuteSqlCommand の文字列補間String interpolation in FromSql and ExecuteSqlCommand

C# 6 では文字列補間が導入されました。この機能では、C# 式を文字列リテラルに直接埋め込むことができます。実行時、文字列が効率的に構築されます。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. EF Core 2.0 では、RAW SQL 文字列を受け取る 2 つのプライマリ API、FromSqlExecuteSqlCommand に、補間文字列の特殊なサポートを追加しました。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. この新しいサポートにより、"安全な" 方法で C# 文字列補間を使用できます。This new support allows C# string interpolation to be used in a "safe" manner. すなわち、実行時に SQL を動的に構築するときに起こりうる SQL 挿入のよくある間違いを防ぎます。That is, in a way that protects against common SQL injection mistakes that can occur when dynamically constructing SQL at runtime.

次に例を示します。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();
  }

この例では、2 つの変数が SQL 形式文字列に埋め込まれています。In this example, there are two variables embedded in the SQL format string. EF Core が次の SQL を生成します。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()

EF Core またはプロバイダーが利用できる EF.Functions プロパティを追加しました。このプロパティは、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. このようなメソッドの最初の例が 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;

Like() にはインメモリ実装が付随することに注目してください。インメモリ データベースに対して作業するときやクライアント側で述語を評価する必要があるときに便利です。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.

データベース管理Database management

DbContext スキャフォールディングの複数形化フックPluralization hook for DbContext scaffolding

EF Core 2.0 では、新しい IPluralizer サービスが導入されました。エンティティ型の名前を単数形化し、DbSet の名前を複数形化するために利用されます。EF Core 2.0 introduces a new IPluralizer service that is used to singularize entity type names and pluralize DbSet names. 既定の実装は no-op (操作なし) です。独自のプルーラライザー (複数形にするもの) を簡単にプラグインできるフックに過ぎません。The default implementation is a no-op, so this is just a hook where folks can easily plug in their own pluralizer.

開発者が独自のプルーラライザーをフックインすると次のようになります。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;
    }
}

OthersOthers

ADO.NET SQLite プロバイダーを SQLitePCL.raw に移動Move ADO.NET SQLite provider to SQLitePCL.raw

これにより、Microsoft.Data.Sqlite で、さまざまプラットフォームでネイティブ SQLite バイナリを配信するためのソリューションがより堅牢になります。This gives us a more robust solution in Microsoft.Data.Sqlite for distributing native SQLite binaries on different platforms.

モデルごとにプロバイダーは 1 つOnly one provider per model

プロバイダーがモデルと対話するしくみが大幅に増強され、規則、注釈、fluent API がさまざまプロバイダーと連動するしくみが単純になりました。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 は、使用されるプロバイダーごとに異なる IModel をビルドするようになりました。EF Core 2.0 will now build a different IModel for each different provider being used. これは通常、アプリケーションに対して透過的です。This is usually transparent to the application. 下位レベルのメタデータ API が単純になり、一般的なリレーショナル メタデータ コンセプトへのあらゆるアクセスが .SqlServer.Sqlite などではなく、.Relational の呼び出しで常に行われます。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.

統合されたログと診断Consolidated logging and diagnostics

ログ記録 (ILogger に基づく) メカニズムと診断 (DiagnosticSource に基づく) メカニズムで共有されるコードが増えました。Logging (based on ILogger) and Diagnostics (based on DiagnosticSource) mechanisms now share more code.

ILogger に送信されるメッセージのイベント ID が 2.0 で変更されました。The event IDs for messages sent to an ILogger have changed in 2.0. イベント ID が EF Core コード全体で一意になりました。The event IDs are now unique across EF Core code. これらのメッセージはまた、たとえば MVC で使用される構造化されたログ記録の標準パターンに従います。These messages now also follow the standard pattern for structured logging used by, for example, MVC.

ロガー カテゴリも変更されました。Logger categories have also changed. 現在、DbLoggerCategory 経由でアクセスされる既知のカテゴリ セットがあります。There is now a well-known set of categories accessed through DbLoggerCategory.

DiagnosticSource イベントでは、それに対応する ILogger メッセージと同じイベント ID 名が使用されるようになりました。DiagnosticSource events now use the same event ID names as the corresponding ILogger messages.