EF Core 5.0의 새로운 기능What's New in EF Core 5.0

EF Core 5.0에 대해 계획된 모든 기능이 완료되었습니다.All features planned for EF Core 5.0 have now been completed. 이 페이지에는 각 미리 보기에서 소개된 중요한 변경 내용의 개요가 포함되어 있습니다.This page contains an overview of interesting changes introduced in each preview.

이 페이지는 EF Core 5.0의 플랜과 중복되지 않습니다.This page does not duplicate the plan for EF Core 5.0. 이 플랜에서는 최종 릴리스를 출시하기 전에 포함할 예정인 모든 항목을 비롯하여 EF Core 5.0의 전체 테마를 설명합니다.The plan describes the overall themes for EF Core 5.0, including everything we are planning to include before shipping the final release.

RC1RC1

다대다Many-to-many

EF Core 5.0은 조인 테이블을 명시적으로 매핑하지 않고 다 대 다 관계를 지원합니다.EF Core 5.0 supports many-to-many relationships without explicitly mapping the join table.

예를 들어 다음과 같은 엔터티 형식을 고려합니다.For example, consider these entity types:

public class Post
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public int Id { get; set; }
    public string Text { get; set; }
    public ICollection<Post> Posts { get; set; }
}

Post에는 Tags 컬렉션이 포함되고 Tag에는 Posts 컬렉션이 포함됩니다.Notice that Post contains a collection of Tags, and Tag contains a collection of Posts. EF Core 5.0에서는 규칙에 따라 이를 다 대 다 관계로 인식합니다.EF Core 5.0 recognizes this as a many-to-many relationship by convention. 즉, OnModelCreating에서 코드가 필요하지 않습니다.This means no code is required in OnModelCreating:

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

마이그레이션(또는 EnsureCreated)을 사용하여 데이터베이스를 만들 때 EF Core에서 조인 테이블을 자동으로 만듭니다.When Migrations (or EnsureCreated) are used to create the database, EF Core will automatically create the join table. 예를 들어 이 모델용 SQL Server에서 EF Core는 다음을 생성합니다.For example, on SQL Server for this model, EF Core generates:

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Name] nvarchar(max) NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([Id])
);

CREATE TABLE [Tag] (
    [Id] int NOT NULL IDENTITY,
    [Text] nvarchar(max) NULL,
    CONSTRAINT [PK_Tag] PRIMARY KEY ([Id])
);

CREATE TABLE [PostTag] (
    [PostsId] int NOT NULL,
    [TagsId] int NOT NULL,
    CONSTRAINT [PK_PostTag] PRIMARY KEY ([PostsId], [TagsId]),
    CONSTRAINT [FK_PostTag_Posts_PostsId] FOREIGN KEY ([PostsId]) REFERENCES [Posts] ([Id]) ON DELETE CASCADE,
    CONSTRAINT [FK_PostTag_Tag_TagsId] FOREIGN KEY ([TagsId]) REFERENCES [Tag] ([Id]) ON DELETE CASCADE
);

CREATE INDEX [IX_PostTag_TagsId] ON [PostTag] ([TagsId]);

BlogPost 엔터티를 만들고 연결하면 조인 테이블 업데이트가 자동으로 수행됩니다.Creating and associating Blog and Post entities results in join table updates happening automatically. 예를 들어:For example:

var beginnerTag = new Tag {Text = "Beginner"};
var advancedTag = new Tag {Text = "Advanced"};
var efCoreTag = new Tag {Text = "EF Core"};

context.AddRange(
    new Post {Name = "EF Core 101", Tags = new List<Tag> {beginnerTag, efCoreTag}},
    new Post {Name = "Writing an EF database provider", Tags = new List<Tag> {advancedTag, efCoreTag}},
    new Post {Name = "Savepoints in EF Core", Tags = new List<Tag> {beginnerTag, efCoreTag}});

context.SaveChanges();

Posts 및 Tags를 삽입하면 EF에서 자동으로 조인 테이블에 행을 만듭니다.After inserting the Posts and Tags, EF will then automatically create rows in the join table. 예를 들어 SQL Server에서는 다음과 같이 표시됩니다.For example, on SQL Server:

SET NOCOUNT ON;
INSERT INTO [PostTag] ([PostsId], [TagsId])
VALUES (@p6, @p7),
(@p8, @p9),
(@p10, @p11),
(@p12, @p13),
(@p14, @p15),
(@p16, @p17);

쿼리의 경우 Include 및 기타 쿼리 작업이 다른 모든 관계에서처럼 작동합니다.For queries, Include and other query operations work just like for any other relationship. 예를 들어:For example:

foreach (var post in context.Posts.Include(e => e.Tags))
{
    Console.Write($"Post \"{post.Name}\" has tags");

    foreach (var tag in post.Tags)
    {
        Console.Write($" '{tag.Text}'");
    }
}

생성된 SQL에서는 조인 테이블을 자동으로 사용하여 모든 관련 Tags를 다시 가져옵니다.The SQL generated uses the join table automatically to bring back all related Tags:

SELECT [p].[Id], [p].[Name], [t0].[PostsId], [t0].[TagsId], [t0].[Id], [t0].[Text]
FROM [Posts] AS [p]
LEFT JOIN (
    SELECT [p0].[PostsId], [p0].[TagsId], [t].[Id], [t].[Text]
    FROM [PostTag] AS [p0]
    INNER JOIN [Tag] AS [t] ON [p0].[TagsId] = [t].[Id]
) AS [t0] ON [p].[Id] = [t0].[PostsId]
ORDER BY [p].[Id], [t0].[PostsId], [t0].[TagsId], [t0].[Id]

EF6와 달리 EF Core는 조인 테이블의 전체 사용자 지정을 허용합니다.Unlike EF6, EF Core allows full customization of the join table. 예를 들어 아래 코드에서는 조인 엔터티에 대한 탐색도 포함되어 있는 다 대 다 관계를 구성하며, 조인 엔터티에는 페이로드 속성이 포함되어 있습니다.For example, the code below configures a many-to-many relationship that also has navigations to the join entity, and in which the join entity contains a payload property:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Community>()
        .HasMany(e => e.Members)
        .WithMany(e => e.Memberships)
        .UsingEntity<PersonCommunity>(
            b => b.HasOne(e => e.Member).WithMany().HasForeignKey(e => e.MembersId),
            b => b.HasOne(e => e.Membership).WithMany().HasForeignKey(e => e.MembershipsId))
        .Property(e => e.MemberSince).HasDefaultValueSql("CURRENT_TIMESTAMP");
}

쿼리에 엔터티 형식 매핑Map entity types to queries

엔터티 형식은 일반적으로 테이블이나 뷰에 매핑되어 EF Core가 해당 형식을 쿼리할 때 테이블이나 뷰의 콘텐츠를 다시 끌어오도록 합니다.Entity types are commonly mapped to tables or views such that EF Core will pull back the contents of the table or view when querying for that type. EF Core 5.0에서는 엔터티 형식이 “정의 쿼리”에 매핑되도록 허용합니다.EF Core 5.0 allows an entity type to mapped to a "defining query". 이 기능이 이전 버전에서는 부분적으로 지원되었지만 EF Core 5.0에서는 많이 향상되고 다른 구문을 사용합니다.(This was partially supported in previous versions, but is much improved and has different syntax in EF Core 5.0.)

예를 들어 한 테이블에는 최신 게시물이 있고 다른 테이블에는 레거시 게시물이 있는 두 개의 테이블을 가정해 보겠습니다.For example, consider two tables; one with modern posts; the other with legacy posts. 최신 게시물 테이블에는 몇 개의 추가 열이 있지만 애플리케이션의 목적에 따라 최신 게시물과 레거시 게시물을 모두 결합하고 필요한 속성이 모두 있는 엔터티 형식에 매핑하려고 합니다.The modern posts table has some additional columns, but for the purpose of our application we want both modern and legacy posts tp be combined and mapped to an entity type with all necessary properties:

public class Post
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Category { get; set; }
    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

EF Core 5.0에서는 ToSqlQuery를 사용하여 두 테이블에서 행을 끌어오고 결합하는 쿼리에 이 엔터티 형식을 매핑할 수 있습니다.In EF Core 5.0, ToSqlQuery can be used to map this entity type to a query that pulls and combines rows from both tables:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>().ToSqlQuery(
        @"SELECT Id, Name, Category, BlogId FROM posts
          UNION ALL
          SELECT Id, Name, ""Legacy"", BlogId from legacy_posts");
}

legacy_posts 테이블에는 Category 열이 없으므로 모든 레거시 게시물에 대한 기본값을 합성합니다.Notice that the legacy_posts table does not have a Category column, so we instead synthesize a default value for all legacy posts.

그런 다음 이 엔터티 형식을 일반적인 방법으로 LINQ 쿼리에 사용할 수 있습니다.This entity type can then be used in the normal way for LINQ queries. 예를 들어 모바일 서비스는 스크립트 실행 간에 상태를 유지하지 않으므로 스크립트 실행 간에 지속되어야 하는 모든 데이터를 테이블에 저장해야 합니다.For example. LINQ 쿼리는 다음과 같이 표시됩니다.the LINQ query:

var posts = context.Posts.Where(e => e.Blog.Name.Contains("Unicorn")).ToList();

SQLite에 다음 SQL을 생성합니다.Generates the following SQL on SQLite:

SELECT "p"."Id", "p"."BlogId", "p"."Category", "p"."Name"
FROM (
    SELECT Id, Name, Category, BlogId FROM posts
    UNION ALL
    SELECT Id, Name, "Legacy", BlogId from legacy_posts
) AS "p"
INNER JOIN "Blogs" AS "b" ON "p"."BlogId" = "b"."Id"
WHERE ('Unicorn' = '') OR (instr("b"."Name", 'Unicorn') > 0)

엔터티 형식에 대해 구성된 쿼리는 전체 LINQ 쿼리 작성을 위한 시작점으로 사용됩니다.Notice that the query configured for the entity type is used as a starting for composing the full LINQ query.

이벤트 카운터Event counters

.NET 이벤트 카운터는 애플리케이션에서 성능 메트릭을 효율적으로 노출하는 방법입니다..NET event counters are a way to efficiently expose performance metrics from an application. EF Core 5.0에서는 Microsoft.EntityFrameworkCore 카테고리 아래에 이벤트 카운터가 포함됩니다.EF Core 5.0 includes event counters under the Microsoft.EntityFrameworkCore category. 예를 들어:For example:

dotnet counters monitor Microsoft.EntityFrameworkCore -p 49496

이렇게 하면 dotnet 카운터에서 프로세스 49496에 대한 EF Core 이벤트를 수집하기 시작합니다.This tells dotnet counters to start collecting EF Core events for process 49496. 그러면 콘솔에 다음과 같은 출력값이 생성됩니다.This generates output like this in the console:

[Microsoft.EntityFrameworkCore]
    Active DbContexts                                               1
    Execution Strategy Operation Failures (Count / 1 sec)           0
    Execution Strategy Operation Failures (Total)                   0
    Optimistic Concurrency Failures (Count / 1 sec)                 0
    Optimistic Concurrency Failures (Total)                         0
    Queries (Count / 1 sec)                                     1,755
    Queries (Total)                                            98,402
    Query Cache Hit Rate (%)                                      100
    SaveChanges (Count / 1 sec)                                     0
    SaveChanges (Total)                                             1

속성 모음Property bags

EF Core 5.0에서는 동일한 CLR 형식을 여러 가지 다른 엔터티 형식에 매핑할 수 있습니다.EF Core 5.0 allows the same CLR type to be mapped to multiple different entity types. 이러한 형식을 공유 형식 엔터티 형식이라고 합니다.Such types are known as shared-type entity types. 인덱서 속성(미리 보기 1에 포함됨)과 결합된 이 기능을 사용하면 속성 모음을 엔터티 형식으로 사용할 수 있습니다.This feature combined with indexer properties (included in preview 1) allows property bags to be used as entity type.

예를 들어 아래의 DbContext에서는 제품과 범주 모두에 대한 공유 형식 엔터티 형식으로 BCL 형식 Dictionary<string, object>를 구성합니다.For example, the DbContext below configures the BCL type Dictionary<string, object> as a shared-type entity type for both products and categories.

public class ProductsContext : DbContext
{
    public DbSet<Dictionary<string, object>> Products => Set<Dictionary<string, object>>("Product");
    public DbSet<Dictionary<string, object>> Categories => Set<Dictionary<string, object>>("Category");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.SharedTypeEntity<Dictionary<string, object>>("Category", b =>
        {
            b.IndexerProperty<string>("Description");
            b.IndexerProperty<int>("Id");
            b.IndexerProperty<string>("Name").IsRequired();
        });

        modelBuilder.SharedTypeEntity<Dictionary<string, object>>("Product", b =>
        {
            b.IndexerProperty<int>("Id");
            b.IndexerProperty<string>("Name").IsRequired();
            b.IndexerProperty<string>("Description");
            b.IndexerProperty<decimal>("Price");
            b.IndexerProperty<int?>("CategoryId");

            b.HasOne("Category", null).WithMany();
        });
    }
}

이제 사전 개체(“속성 모음”)를 엔터티 인스턴스로 컨텍스트에 추가하고 저장할 수 있습니다.Dictionary objects ("property bags") can now be added to the context as entity instances and saved. 예를 들어:For example:

var beverages = new Dictionary<string, object>
{
    ["Name"] = "Beverages",
    ["Description"] = "Stuff to sip on"
};

context.Categories.Add(beverages);

context.SaveChanges();

이러한 엔터티를 일반적인 방식으로 쿼리하고 업데이트할 수 있습니다.These entities can then be queried and updated in the normal way:

var foods = context.Categories.Single(e => e["Name"] == "Foods");
var marmite = context.Products.Single(e => e["Name"] == "Marmite");

marmite["CategoryId"] = foods["Id"];
marmite["Description"] = "Yummy when spread _thinly_ on buttered Toast!";

context.SaveChanges();

SaveChanges 인터셉션 및 이벤트SaveChanges interception and events

EF Core 5.0에서는 SaveChanges가 호출될 때 트리거되는 .NET 이벤트와 EF Core 인터셉터를 모두 소개합니다.EF Core 5.0 introduces both .NET events and an EF Core interceptor triggered when SaveChanges is called.

이벤트는 사용하기가 쉽습니다. 예를 들면 다음과 같습니다.The events are simple to use; for example:

context.SavingChanges += (sender, args) =>
{
    Console.WriteLine($"Saving changes for {((DbContext)sender).Database.GetConnectionString()}");
};

context.SavedChanges += (sender, args) =>
{
    Console.WriteLine($"Saved {args.EntitiesSavedCount} changes for {((DbContext)sender).Database.GetConnectionString()}");
};

다음에 유의합니다.Notice that:

  • 이벤트 전송자는 DbContext 인스턴스입니다.The event sender is the DbContext instance
  • SavedChanges 이벤트에 대한 인수에는 데이터베이스에 저장된 엔터티 수가 포함됩니다.The args for the SavedChanges event contains the number of entities saved to the database

인터셉터는 ISaveChangesInterceptor에 의해 정의되지만 SaveChangesInterceptor에서 상속하여 모든 메서드를 구현하지 않도록 하는 것이 편리하기도 합니다.The interceptor is defined by ISaveChangesInterceptor, but it is often convienient to inherit from SaveChangesInterceptor to avoid implementing every method. 예를 들어:For example:

public class MySaveChangesInterceptor : SaveChangesInterceptor
{
    public override InterceptionResult<int> SavingChanges(
        DbContextEventData eventData,
        InterceptionResult<int> result)
    {
        Console.WriteLine($"Saving changes for {eventData.Context.Database.GetConnectionString()}");

        return result;
    }

    public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
        DbContextEventData eventData,
        InterceptionResult<int> result,
        CancellationToken cancellationToken = new CancellationToken())
    {
        Console.WriteLine($"Saving changes asynchronously for {eventData.Context.Database.GetConnectionString()}");

        return new ValueTask<InterceptionResult<int>>(result);
    }
}

다음에 유의합니다.Notice that:

  • 인터셉터에 동기화 메서드와 비동기 메서드가 모두 있습니다.The interceptor has both sync and async methods. 따라서 감사 서버에 쓰기와 같이 비동기 I/O를 수행해야 하는 경우에 유용할 수 있습니다.This can be useful if you need to perform async I/O, such as writing to an audit server.
  • 인터셉터를 사용하면 모든 인터셉터에 공통되는 InterceptionResult 메커니즘을 사용하여 SaveChanges를 건너뛸 수 있습니다.The interceptor allows SaveChanges to be skipped using the InterceptionResult mechanism common to all interceptors.

인터셉터의 단점은 생성될 때 DbContext에 등록되어야 한다는 점입니다.The downside of interceptors is that they must be registered on the DbContext when it is being constructed. 예를 들어:For example:

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .AddInterceptors(new MySaveChangesInterceptor())
            .UseSqlite("Data Source = test.db");

반면 이벤트는 언제든지 DbContext 인스턴스에 등록할 수 있습니다.In contrast, the events can be registered on the DbContext instance at any time.

마이그레이션에서 테이블 제외Exclude tables from migrations

여러 DbContext에서 단일 엔터티 형식을 매핑하는 것이 유용한 경우가 있습니다.It is sometimes useful to have a single entity type mapped in multiple DbContexts. 바인딩된 컨텍스트를 사용하는 경우에 특히 유용한데, 일반적으로 각 바인딩된 컨텍스에 다른 DbContext 형식을 사용하기 때문입니다.This is especially true when using bounded contexts, for which it is common to have a different DbContext type for each bounded context.

예를 들어 User 형식은 권한 부여 컨텍스트와 보고 컨텍스트 모두에 필요할 수 있습니다.For example, a User type may be needed by both an authorization context and a reporting context. User 형식을 변경하면 두 DbContext에 대한 마이그레이션에서 데이터베이스를 업데이트하려고 합니다.If a change is made to the User type, then migrations for both DbContexts will attempt to update the database. 이를 방지하려면 마이그레이션에서 테이블을 제외하도록 컨텍스트 중 하나에 대한 모델을 구성할 수 있습니다.To prevent this, the model for one of the contexts can be configured to exclude the table from its migrations.

아래 코드에서 AuthorizationContextUsers 테이블의 변경 내용에 대한 마이그레이션을 생성하지만 ReportingContext는 생성하지 않아 마이그레이션이 충돌하지 않도록 합니다.In the code below, the AuthorizationContext will generate migrations for changes to the Users table, but the ReportingContext will not, preventing the migrations from clashing.

public class AuthorizationContext : DbContext
{
    public DbSet<User> Users { get; set; }
}

public class ReportingContext : DbContext
{
    public DbSet<User> Users { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>().ToTable("Users", t => t.ExcludeFromMigrations());
    }
}

1:1 종속 항목 필요Required 1:1 dependents

EF Core 3.1에서 일 대 일 관계의 종속 끝은 항상 선택 사항으로 간주되었습니다.In EF Core 3.1, the dependent end of a one-to-one relationship was always considered optional. 이는 소유된 엔터티를 사용할 때 가장 명확하게 드러났습니다.This was most apparent when using owned entities. 예를 들어 다음과 같은 모델 및 구성을 가정해 보겠습니다.For example, consider the following model and configuration:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

    public Address HomeAddress { get; set; }
    public Address WorkAddress { get; set; }
}

public class Address
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string City { get; set; }
    public string Region { get; set; }
    public string Country { get; set; }
    public string Postcode { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>(b =>
    {
        b.OwnsOne(e => e.HomeAddress,
            b =>
            {
                b.Property(e => e.Line1).IsRequired();
                b.Property(e => e.City).IsRequired();
                b.Property(e => e.Region).IsRequired();
                b.Property(e => e.Postcode).IsRequired();
            });

        b.OwnsOne(e => e.WorkAddress);
    });
}

마이그레이션에서 SQLite에 대해 다음과 같은 테이블을 만듭니다.This results in Migrations creating the following table for SQLite:

CREATE TABLE "People" (
    "Id" INTEGER NOT NULL CONSTRAINT "PK_People" PRIMARY KEY AUTOINCREMENT,
    "Name" TEXT NULL,
    "HomeAddress_Line1" TEXT NULL,
    "HomeAddress_Line2" TEXT NULL,
    "HomeAddress_City" TEXT NULL,
    "HomeAddress_Region" TEXT NULL,
    "HomeAddress_Country" TEXT NULL,
    "HomeAddress_Postcode" TEXT NULL,
    "WorkAddress_Line1" TEXT NULL,
    "WorkAddress_Line2" TEXT NULL,
    "WorkAddress_City" TEXT NULL,
    "WorkAddress_Region" TEXT NULL,
    "WorkAddress_Country" TEXT NULL,
    "WorkAddress_Postcode" TEXT NULL
);

일부 HomeAddress 속성이 필수로 구성된 경우에도 모든 열이 null을 허용합니다.Notice that all the columns are nullable, even though some of the HomeAddress properties have been configured as required. 또한 Person을 쿼리할 때 집 주소나 회사 주소의 모든 열이 null이면 EF Core에서는 Address의 빈 인스턴스를 설정하는 대신 HomeAddress 및/또는 WorkAddress 속성을 null로 유지합니다.Also, when querying for a Person, if all the columns for either the home or work address are null, then EF Core will leave the HomeAddress and/or WorkAddress properties as null, rather than setting an empty instance of Address.

EF Core 5.0에서는 HomeAddress 탐색을 필요한 종속 항목으로 구성할 수 있습니다.In EF Core 5.0, the HomeAddress navigation can now be configured as as a required dependent. 예를 들어:For example:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>(b =>
    {
        b.OwnsOne(e => e.HomeAddress,
            b =>
            {
                b.Property(e => e.Line1).IsRequired();
                b.Property(e => e.City).IsRequired();
                b.Property(e => e.Region).IsRequired();
                b.Property(e => e.Postcode).IsRequired();
            });
        b.Navigation(e => e.HomeAddress).IsRequired();

        b.OwnsOne(e => e.WorkAddress);
    });
}

이제 마이그레이션에 의해 생성된 테이블에 필요한 종속 항목의 필수 속성에 대해 null을 허용하지 않는 열이 포함됩니다.The table created by Migrations will now included non-nullable columns for the required properties of the required dependent:

CREATE TABLE "People" (
    "Id" INTEGER NOT NULL CONSTRAINT "PK_People" PRIMARY KEY AUTOINCREMENT,
    "Name" TEXT NULL,
    "HomeAddress_Line1" TEXT NOT NULL,
    "HomeAddress_Line2" TEXT NULL,
    "HomeAddress_City" TEXT NOT NULL,
    "HomeAddress_Region" TEXT NOT NULL,
    "HomeAddress_Country" TEXT NULL,
    "HomeAddress_Postcode" TEXT NOT NULL,
    "WorkAddress_Line1" TEXT NULL,
    "WorkAddress_Line2" TEXT NULL,
    "WorkAddress_City" TEXT NULL,
    "WorkAddress_Region" TEXT NULL,
    "WorkAddress_Country" TEXT NULL,
    "WorkAddress_Postcode" TEXT NULL
);

또한 null이 필요한 종속 항목이 있는 소유자를 저장하려고 하면 EF Core에서 예외를 throw합니다.In addition, EF Core will now throw an exception if an attempt is made to save an owner which has a null required dependent. 이 예제에서 EF Core는 HomeAddress가 null인 Person을 저장하려고 할 때 예외를 throw합니다.In this example, EF Core will throw when attempting to save a Person with a null HomeAddress.

마지막으로 필요한 종속 항목의 모든 열에 null 값이 있는 경우에도 EF Core는 필요한 종속 항목의 인스턴스를 만듭니다.Finally, EF Core will still create an instance of a required dependent even when all the columns for the required dependent have null values.

마이그레이션 생성 옵션Options for migration generation

EF Core 5.0에서는 여러 가지 용도로 마이그레이션 생성에 대한 제어가 강화되었습니다.EF Core 5.0 introduces greater control over generation of migrations for different purposes. 이 메서드의 기능은 다음과 같습니다.This includes the ability to:

  • 마이그레이션이 스크립트용으로 생성되고 있는지 즉시 실행용으로 생성되고 있는지 확인Know if the migration is being generated for a script or for immediate execution
  • idempotent 스크립트가 생성되고 있는지 확인Know if an idempotent script is being generated
  • 스크립트에서 트랜잭션 문을 제외해야 하는지 확인(아래의 ‘트랜잭션이 포함된 마이그레이션 스크립트’ 참조)Know if the script should exclude transaction statements (See Migrations scripts with transactions below.)

이 동작은 MigrationsSqlGenerationOptions 열거형으로 지정되며 IMigrator.GenerateScript에 전달할 수 있습니다.This behavior is specified by an the MigrationsSqlGenerationOptions enum, which can now be passed to IMigrator.GenerateScript.

이 작업에도 포함되어 필요할 때 SQL Server에서 EXEC를 호출하므로 idempotent 스크립트의 생성이 향상됩니다.Also included in this work is better generation of idempotent scripts with calls to EXEC on SQL Server when needed. 또한 이 작업을 사용하면 PostgreSQL을 비롯한 다른 데이터베이스 공급자에 의해 생성된 스크립트의 기능이 유사하게 향상됩니다.This work also enables similar improvements to the scripts generated by other database providers, including PostgreSQL.

트랜잭션이 포함된 마이그레이션 스크립트Migrations scripts with transactions

이제 마이그레이션에서 생성된 SQL 스크립트에 트랜잭션을 시작하고 커밋하는 문이 마이그레이션에 적합하게 포함됩니다.SQL scripts generated from migrations now contain statements to begin and commit transactions as appropriate for the migration. 예를 들어 아래의 마이그레이션 스크립트는 두 마이그레이션에서 생성되었습니다.For example, the migration script below was generated from two migrations. 이제 각 마이그레이션이 트랜잭션 내에서 적용됩니다.Notice that each migration is now applied inside a transaction.

BEGIN TRANSACTION;
GO

CREATE TABLE [Groups] (
    [Id] int NOT NULL IDENTITY,
    [Name] nvarchar(max) NULL,
    CONSTRAINT [PK_Groups] PRIMARY KEY ([Id])
);
GO

CREATE TABLE [Members] (
    [Id] int NOT NULL IDENTITY,
    [Name] nvarchar(max) NULL,
    [GroupId] int NULL,
    CONSTRAINT [PK_Members] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Members_Groups_GroupId] FOREIGN KEY ([GroupId]) REFERENCES [Groups] ([Id]) ON DELETE NO ACTION
);
GO

CREATE INDEX [IX_Members_GroupId] ON [Members] ([GroupId]);
GO

INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
VALUES (N'20200910194835_One', N'6.0.0-alpha.1.20460.2');
GO

COMMIT;
GO

BEGIN TRANSACTION;
GO

EXEC sp_rename N'[Groups].[Name]', N'GroupName', N'COLUMN';
GO

INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
VALUES (N'20200910195234_Two', N'6.0.0-alpha.1.20460.2');
GO

COMMIT;

이전 섹션에서 설명한 대로 트랜잭션을 다르게 처리해야 하는 경우에는 이 트랜잭션 사용을 사용하지 않도록 설정할 수 있습니다.As mentioned in the previous section, this use of transactions can be disabled if transactions need to be handled differently.

보류 중인 마이그레이션 보기See pending migrations

이 기능은 커뮤니티에서 @Psypher9가 제공했습니다.This feature was contributed from the community by @Psypher9. 기여해 주셔서 감사합니다.Many thanks for the contribution!

이제 dotnet ef migrations list 명령은 데이터베이스에 아직 적용되지 않은 마이그레이션을 보여 줍니다.The dotnet ef migrations list command now shows which migrations have not yet been applied to the database. 예를 들어:For example:

ajcvickers@avickers420u:~/AllTogetherNow/Daily$ dotnet ef migrations list
Build started...
Build succeeded.
20200910201647_One
20200910201708_Two
20200910202050_Three (Pending)
ajcvickers@avickers420u:~/AllTogetherNow/Daily$

또한 패키지 관리자 콘솔에서 동일한 기능을 제공하는 Get-Migration 명령도 있습니다.In addition, there is now a Get-Migration command for the Package Manager Console with the same functionality.

값 비교자용 ModelBuilder APIModelBuilder API for value comparers

사용자 지정 변경 가능 형식에 대한 EF Core 속성에서 속성 변경 내용을 올바르게 검색하려면 값 비교자가 필요합니다.EF Core properties for custom mutable types require a value comparer for property changes to be detected correctly. 이제 형식에 대한 값 변환 구성의 일부로 지정할 수 있습니다.This can now be specified as part of configuring the value conversion for the type. 예를 들어:For example:

modelBuilder
    .Entity<EntityType>()
    .Property(e => e.MyProperty)
    .HasConversion(
        v => JsonSerializer.Serialize(v, null),
        v => JsonSerializer.Deserialize<List<int>>(v, null),
        new ValueComparer<List<int>>(
            (c1, c2) => c1.SequenceEqual(c2),
            c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
            c => c.ToList()));

EntityEntry TryGetValue 메서드EntityEntry TryGetValue methods

이 기능은 커뮤니티에서 @m4ss1m0g가 제공했습니다.This feature was contributed from the community by @m4ss1m0g. 기여해 주셔서 감사합니다.Many thanks for the contribution!

TryGetValue 메서드가 EntityEntry.CurrentValuesEntityEntry.OriginalValues에 추가되었습니다.A TryGetValue method has been added to EntityEntry.CurrentValues and EntityEntry.OriginalValues. 따라서 속성이 EF 모델에서 매핑되는지를 먼저 확인하지 않고도 속성 값을 요청할 수 있습니다.This allows the value of a property to be requested without first checking if the property is mapped in the EF model. 예를 들어:For example:

if (entry.CurrentValues.TryGetValue(propertyName, out var value))
{
    Console.WriteLine(value);
}

SQL Server의 최대 일괄 처리 크기 기본값Default max batch size for SQL Server

EF Core 5.0부터는 SQL Server의 SaveChanges의 최대 일괄 처리 크기 기본값이 42입니다.Starting with EF Core 5.0, the default maximum batch size for SaveChanges on SQL Server is now 42. 잘 알려진대로, 이 숫자는 인생, 우주, 모든 것의 궁극적인 질문에 대한 해답(the Ultimate Question of Life, the Universe, and Everything)이다.As is well known, this is also the answer to the Ultimate Question of Life, the Universe, and Everything. 그러나 일괄 처리 성능 분석을 통해 이 값을 가져왔기 때문에 우연의 일치일 수도 있습니다.However, this is probably a coincidence, since the value was obtained through analysis of batching performance. SQL Server가 왜 이런 방식으로 작동하는지 이해하도록 지구가 생성되었다고 하면 약간 그럴듯해 보이기도 하겠지만, 우리가 궁극적 질문의 형식을 발견했다고 생각하지는 않습니다.We do not believe that we have discovered a form of the Ultimate Question, although it does seem somewhat plausible that the Earth was created to understand why SQL Server works the way it does.

기본 개발 환경Default environment to Development

이제 EF Core 명령줄 도구에서 ASPNETCORE_ENVIRONMENTDOTNET_ENVIRONMENT 환경 변수를 “Development”로 자동으로 구성합니다.The EF Core command line tools now automatically configure the ASPNETCORE_ENVIRONMENT and DOTNET_ENVIRONMENT environment variables to "Development". 따라서 개발 중 ASP.NET Core 환경에서 제네릭 호스트를 사용하면 환경이 제공됩니다.This brings the experience when using the generic host in line with the experience for ASP.NET Core during development. #19903을 참조하세요.See #19903.

마이그레이션 열 순서 향상Better migrations column ordering

이제 매핑되지 않은 기본 클래스에 대한 열은 매핑된 엔터티 형식에 대한 다른 열 다음으로 순서가 지정됩니다.The columns for unmapped base classes are now ordered after other columns for mapped entity types. 이 동작은 새로 만들어진 테이블에만 영향을 줍니다.Note this only impacts newly created tables. 기존 테이블의 열 순서는 변경되지 않고 그대로 유지됩니다.The column order for existing tables remains unchanged. #11314를 참조하세요.See #11314.

쿼리 기능 향상Query improvements

EF Core 5.0 RC1에서는 몇 가지 추가 쿼리 변환 기능이 향상되었습니다.EF Core 5.0 RC1 contains some additional query translation improvements:

  • Cosmos에서 is의 변환--#16391 참조Translation of is on Cosmos--see #16391
  • 사용자 매핑 함수에 주석을 추가하여 null 전파 제어--#19609 참조User-mapped functions can now be annotated to control null propagation--see #19609
  • 조건부 집계를 사용하여 GroupBy 변환 지원--#11711 참조Support for translation of GroupBy with conditional aggregates--see #11711
  • 집계 전에 그룹 요소에 대한 Distinct 연산자 변환--#17376 참조Translation of Distinct operator over group element before aggregate--see #17376

필드에 대한 모델 작성Model building for fields

마지막으로 RC1에서 EF Core를 사용하면 속성뿐만 아니라 필드에 대한 ModelBuilder에서 람다 메서드를 사용할 수 있습니다.Finally for RC1, EF Core now allows use of the lambda methods in the ModelBuilder for fields as well as properties. 예를 들어 몇 가지 이유로 속성을 원하지 않고 퍼블릭 필드를 사용하기로 한 경우 람다 작성기를 사용하여 이러한 필드를 매핑할 수 있습니다.For example, if you are averse to properties for some reason and decide to use public fields, then these fields can now be mapped using the lambda builders:

public class Post
{
    public int Id;
    public string Name;
    public string Category;
    public int BlogId;
    public Blog Blog;
}

public class Blog
{
    public int Id;
    public string Name;
    public ICollection<Post> Posts;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>(b =>
    {
        b.Property(e => e.Id);
        b.Property(e => e.Name);
    });

    modelBuilder.Entity<Post>(b =>
    {
        b.Property(e => e.Id);
        b.Property(e => e.Name);
        b.Property(e => e.Category);
        b.Property(e => e.BlogId);
        b.HasOne(e => e.Blog).WithMany(e => e.Posts);
    });
}

이제 이 기능을 사용할 수 있지만 이 방법을 권장하지는 않습니다.While this is now possible, we are certainly not recommending that you do this. 또한 EF Core에 추가 필드 매핑 기능을 추가하지 않기 때문에 문자열 메서드를 항상 요구하는 대신 람다 메서드를 사용할 수 있습니다.Also, note that this does not add any additional field mapping capabilities to EF Core, it only allows the lambda methods to be used instead of always requiring the string methods. 필드가 거의 퍼블릭이 아니기 때문에 유용하지 않기도 합니다.This is seldom useful since fields are rarely public.

미리 보기 8Preview 8

TPT(형식당 테이블) 매핑Table-per-type (TPT) mapping

기본적으로 EF Core는 .NET 형식의 상속 계층 구조를 단일 데이터베이스 테이블에 매핑합니다.By default, EF Core maps an inheritance hierarchy of .NET types to a single database table. 이를 TPH(계층 구조당 하나의 테이블) 매핑이라고 합니다.This is known as table-per-hierarchy (TPH) mapping. EF Core 5.0에서는 상속 계층 구조의 각 .NET 형식을 다른 데이터베이스 테이블에 매핑할 수 있으며, 이를 TPT(형식당 하나의 테이블) 매핑이라고 합니다.EF Core 5.0 also allows mapping each .NET type in an inheritance hierarchy to a different database table; known as table-per-type (TPT) mapping.

예를 들어 매핑된 계층 구조가 포함된 다음 모델을 살펴보겠습니다.For example, consider this model with a mapped hierarchy:

public class Animal
{
    public int Id { get; set; }
    public string Species { get; set; }
}

public class Pet : Animal
{
    public string Name { get; set; }
}

public class Cat : Pet
{
    public string EducationLevel { get; set; }
}

public class Dog : Pet
{
    public string FavoriteToy { get; set; }
}

기본적으로 EF Core는 단일 테이블에 매핑됩니다.By default, EF Core will map this to a single table:

CREATE TABLE [Animals] (
    [Id] int NOT NULL IDENTITY,
    [Species] nvarchar(max) NULL,
    [Discriminator] nvarchar(max) NOT NULL,
    [Name] nvarchar(max) NULL,
    [EdcuationLevel] nvarchar(max) NULL,
    [FavoriteToy] nvarchar(max) NULL,
    CONSTRAINT [PK_Animals] PRIMARY KEY ([Id])
);

그러나 각 엔터티 형식을 다른 테이블에 매핑하면 형식당 하나의 테이블이 생성됩니다.However, mapping each entity type to a different table will instead result in one table per type:

CREATE TABLE [Animals] (
    [Id] int NOT NULL IDENTITY,
    [Species] nvarchar(max) NULL,
    CONSTRAINT [PK_Animals] PRIMARY KEY ([Id])
);

CREATE TABLE [Pets] (
    [Id] int NOT NULL,
    [Name] nvarchar(max) NULL,
    CONSTRAINT [PK_Pets] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Pets_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id]) ON DELETE NO ACTION
);

CREATE TABLE [Cats] (
    [Id] int NOT NULL,
    [EdcuationLevel] nvarchar(max) NULL,
    CONSTRAINT [PK_Cats] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Cats_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id]) ON DELETE NO ACTION,
    CONSTRAINT [FK_Cats_Pets_Id] FOREIGN KEY ([Id]) REFERENCES [Pets] ([Id]) ON DELETE NO ACTION
);

CREATE TABLE [Dogs] (
    [Id] int NOT NULL,
    [FavoriteToy] nvarchar(max) NULL,
    CONSTRAINT [PK_Dogs] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Dogs_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id]) ON DELETE NO ACTION,
    CONSTRAINT [FK_Dogs_Pets_Id] FOREIGN KEY ([Id]) REFERENCES [Pets] ([Id]) ON DELETE NO ACTION
);

미리 보기 8의 경우 코드 분기 뒤에 위에 나온 외래 키 제약 조건 만들기가 추가되었습니다.Note that creation of the foreign key constraints shown above were added after branching the code for preview 8.

매핑 특성을 사용하여 엔터티 형식을 서로 다른 테이블에 매핑할 수 있습니다.Entity types can be mapped to different tables using mapping attributes:

[Table("Animals")]
public class Animal
{
    public int Id { get; set; }
    public string Species { get; set; }
}

[Table("Pets")]
public class Pet : Animal
{
    public string Name { get; set; }
}

[Table("Cats")]
public class Cat : Pet
{
    public string EdcuationLevel { get; set; }
}

[Table("Dogs")]
public class Dog : Pet
{
    public string FavoriteToy { get; set; }
}

또는 ModelBuilder 구성을 사용합니다.Or using ModelBuilder configuration:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Animal>().ToTable("Animals");
    modelBuilder.Entity<Pet>().ToTable("Pets");
    modelBuilder.Entity<Cat>().ToTable("Cats");
    modelBuilder.Entity<Dog>().ToTable("Dogs");
}

설명서는 이슈 #1979에서 찾아볼 수 있습니다.Documentation is tracked by issue #1979.

마이그레이션: SQLite 테이블 다시 빌드Migrations: Rebuild SQLite tables

다른 데이터베이스와 비교할 때 SQLite는 스키마 조작 기능 면에서 비교적 제한적입니다.Compared to other database, SQLite is relatively limited in its schema manipulation capabilities. 예를 들어 기존 테이블에서 열을 삭제하려면 전체 테이블을 삭제하고 다시 만들어야 합니다.For example, dropping a column from an existing table requires that the entire table be dropped and re-created. EF Core 5.0 마이그레이션은 이제 테이블 재작성이 필요한 스키마 변경이 발생하는 경우 테이블이 자동으로 다시 작성되도록 지원합니다.EF Core 5.0 Migrations now supports automatic rebuilding the table for schema changes that require it.

예를 들어 Unicorn 엔터티 형식에 대해 Unicorns 테이블을 만들었다고 가정합니다.For example, imagine we have a Unicorns table created for a Unicorn entity type:

public class Unicorn
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}
CREATE TABLE "Unicorns" (
    "Id" INTEGER NOT NULL CONSTRAINT "PK_Unicorns" PRIMARY KEY AUTOINCREMENT,
    "Name" TEXT NULL,
    "Age" INTEGER NOT NULL
);

그런 다음, 유니콘의 보존 기간을 저장하는 것이 매우 잘못되었다고 판단하여 해당 속성을 제거하고 새 마이그레이션을 추가하고 데이터베이스를 업데이트해 보겠습니다.We then learn that storing the age of a unicorn is considered very rude, so let's remove that property, add a new migration, and update the database. EF Core 3.1을 사용할 때는 해당 열을 삭제할 수 없기 때문에 이 업데이트에 실패합니다.This update will fail when using EF Core 3.1 because the column cannot be dropped. EF Core 5.0의 마이그레이션에서는 테이블을 다시 작성합니다.In EF Core 5.0, Migrations will instead rebuild the table:

CREATE TABLE "ef_temp_Unicorns" (
    "Id" INTEGER NOT NULL CONSTRAINT "PK_Unicorns" PRIMARY KEY AUTOINCREMENT,
    "Name" TEXT NULL
);

INSERT INTO "ef_temp_Unicorns" ("Id", "Name")
SELECT "Id", "Name"
FROM Unicorns;

PRAGMA foreign_keys = 0;

DROP TABLE "Unicorns";

ALTER TABLE "ef_temp_Unicorns" RENAME TO "Unicorns";

PRAGMA foreign_keys = 1;

다음에 유의합니다.Notice that:

  • 새 테이블용으로 원하는 스키마를 사용하여 임시 테이블이 생성됨A temporary table is created with the desired schema for the new table
  • 현재 테이블의 데이터가 임시 테이블로 복사됨Data is copied from the current table into the temporary table
  • 외래 키 적용이 해제됨Foreign key enforcement is switched off
  • 현재 테이블이 삭제됨The current table is dropped
  • 임시 테이블의 이름이 새 테이블 이름으로 바뀜The temporary table is renamed to be the new table

설명서는 이슈 #2583에서 찾아볼 수 있습니다.Documentation is tracked by issue #2583.

테이블 반환 함수Table-valued functions

이 기능은 커뮤니티에서 @pmiddleton가 제공했습니다.This feature was contributed from the community by @pmiddleton. 기여해 주셔서 감사합니다.Many thanks for the contribution!

EF Core 5.0에서는 .NET 메서드를 TVF(테이블 반환 함수)에 매핑하도록 최고 수준으로 지원합니다.EF Core 5.0 includes first-class support for mapping .NET methods to table-valued functions (TVFs). 그런 다음, 해당 함수를 LINQ 쿼리에 사용할 수 있습니다. 이때 함수의 결과에 대한 추가 컴퍼지션이 SQL로 변환됩니다.These functions can then be used in LINQ queries where additional composition on the results of the function will also be translated to SQL.

예를 들어 SQL Server 데이터베이스에서 정의된 다음 TVF를 살펴보겠습니다.For example, consider this TVF defined in a SQL Server database:

CREATE FUNCTION GetReports(@employeeId int)
RETURNS @reports TABLE
(
    Name nvarchar(50) NOT NULL,
    IsDeveloper bit NOT NULL
)
AS
BEGIN
    WITH cteEmployees AS
    (
        SELECT Id, Name, ManagerId, IsDeveloper
        FROM Employees
        WHERE Id = @employeeId
        UNION ALL
        SELECT e.Id, e.Name, e.ManagerId, e.IsDeveloper
        FROM Employees e
        INNER JOIN cteEmployees cteEmp ON cteEmp.Id = e.ManagerId
    )
    INSERT INTO @reports
    SELECT Name, IsDeveloper
    FROM cteEmployees
    WHERE Id != @employeeId

    RETURN
END

이 TVF를 사용하려면 EF Core 모델에 다음과 같은 두 개의 엔터티 형식이 필요합니다.The EF Core model requires two entity types to use this TVF:

  • 일반적인 방법으로 Employees 테이블에 매핑되는 Employee 형식An Employee type that maps to the Employees table in the normal way
  • TVF에서 반환된 모양과 일치하는 Report 형식A Report type that matches the shape returned by the TVF
public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool IsDeveloper { get; set; }

    public int? ManagerId { get; set; }
    public virtual Employee Manager { get; set; }
}
public class Report
{
    public string Name { get; set; }
    public bool IsDeveloper { get; set; }
}

다음 형식을 EF Core 모델에 포함해야 합니다.These types must be included in the EF Core model:

modelBuilder.Entity<Employee>();
modelBuilder.Entity(typeof(Report)).HasNoKey();

Report는 기본 키가 없으므로 이에 맞게 구성해야 합니다.Notice that Report has no primary key and so must be configured as such.

마지막으로 .NET 메서드를 데이터베이스의 TVF에 매핑해야 합니다.Finally, a .NET method must be mapped to the TVF in the database. 이 메서드는 새 FromExpression 메서드를 사용하여 DbContext에서 정의할 수 있습니다.This method can be defined on the DbContext using the new FromExpression method:

public IQueryable<Report> GetReports(int managerId)
    => FromExpression(() => GetReports(managerId));

다음 메서드는 위에 정의된 TVF와 일치하는 매개 변수 및 반환 형식을 사용합니다.This method uses a parameter and return type that match the TVF defined above. 그런 다음, 이 메서드는 OnModelCreating에서 EF Core 모델에 추가됩니다.The method is then added to the EF Core model in OnModelCreating:

modelBuilder.HasDbFunction(() => GetReports(default));

(여기서 람다를 사용하여 EF Core에 MethodInfo를 쉽게 전달할 수 있습니다.(Using a lambda here is an easy way to pass the MethodInfo to EF Core. 메서드에 전달된 인수는 무시됩니다.)The arguments passed to the method are ignored.)

이제 GetReports를 호출하고 결과를 작성하는 쿼리를 작성할 수 있습니다.We can now write queries that call GetReports and compose over the results. 예를 들면 다음과 같습니다.For example:

from e in context.Employees
from rc in context.GetReports(e.Id)
where rc.IsDeveloper == true
select new
{
  ManagerName = e.Name,
  EmployeeName = rc.Name,
})

SQL Server에서는 다음과 같이 변환됩니다.On SQL Server, this translates to:

SELECT [e].[Name] AS [ManagerName], [g].[Name] AS [EmployeeName]
FROM [Employees] AS [e]
CROSS APPLY [dbo].[GetReports]([e].[Id]) AS [g]
WHERE [g].[IsDeveloper] = CAST(1 AS bit)

SQL은 Employees 테이블에 루팅되어 GetReports를 호출한 후 함수 결과에 WHERE 절을 추가합니다.Notice that the SQL is rooted in the Employees table, calls GetReports, and then adds an additional WHERE clause on the results of the function.

유연한 쿼리/업데이트 매핑Flexible query/update mapping

EF Core 5.0에서는 서로 다른 데이터베이스 개체에 동일한 엔터티 형식을 매핑할 수 있습니다.EF Core 5.0 allows mapping the same entity type to different database objects. 개체는 테이블, 뷰 또는 함수일 수 있습니다.These objects may be tables, views, or functions.

예를 들어 엔터티 형식을 데이터베이스 뷰와 데이터베이스 테이블 모두에 매핑할 수 있습니다.For example, an entity type can be mapped to both a database view and a database table:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Blog>()
        .ToTable("Blogs")
        .ToView("BlogsView");
}

그런 다음, 기본적으로 EF Core는 뷰에서 쿼리하고 테이블에 업데이트를 보냅니다.By default, EF Core will then query from the view and send updates to the table. 예를 들어, 다음 코드를 실행합니다.For example, executing the following code:

var blog = context.Set<Blog>().Single(e => e.Name == "One Unicorn");

blog.Name = "1unicorn2";

context.SaveChanges();

뷰에 대한 쿼리의 결과가 생성되면 테이블에 업데이트합니다.Results in a query against the view, and then an update to the table:

SELECT TOP(2) [b].[Id], [b].[Name], [b].[Url]
FROM [BlogsView] AS [b]
WHERE [b].[Name] = N'One Unicorn'

SET NOCOUNT ON;
UPDATE [Blogs] SET [Name] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

컨텍스트 전반의 분할 쿼리 구성Context-wide split-query configuration

DbContext가 실행하는 쿼리에 대해 이제 분할 쿼리(아래 참조)를 기본값으로 구성할 수 있습니다.Split queries (see below) can now be configured as the default for any query executed by the DbContext. 이 구성은 관계형 공급자에 대해서만 사용할 수 있으므로 UseProvider 구성의 일부로 지정해야 합니다.This configuration is only available for relational providers, and so must be specified as part of the UseProvider configuration. 예를 들면 다음과 같습니다.For example:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseSqlServer(
            Your.SqlServerConnectionString,
            b => b.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));

설명서는 이슈 #2407에서 찾아볼 수 있습니다.Documentation is tracked by issue #2407.

PhysicalAddress 매핑PhysicalAddress mapping

이 기능은 커뮤니티에서 @ralmsdeveloper가 제공했습니다.This feature was contributed from the community by @ralmsdeveloper. 기여해 주셔서 감사합니다.Many thanks for the contribution!

이제 표준 .NET PhysicalAddress 클래스가 기본 지원이 아직 없는 데이터베이스의 문자열 열에 자동으로 매핑됩니다.The standard .NET PhysicalAddress class is now automatically mapped to a string column for databases that do not already have native support. 자세한 내용은 다음 IPAddress의 예제를 참조하세요.For more information, see the examples for IPAddress below.

미리 보기 7Preview 7

DbContextFactoryDbContextFactory

EF Core 5.0에는 애플리케이션의 D.I.(종속성 주입) 컨테이너에 DbContext 인스턴스를 만들기 위한 팩터리를 등록하는 AddDbContextFactoryAddPooledDbContextFactory를 도입되었습니다.EF Core 5.0 introduces AddDbContextFactory and AddPooledDbContextFactory to register a factory for creating DbContext instances in the application's dependency injection (D.I.) container. 예를 들어:For example:

services.AddDbContextFactory<SomeDbContext>(b =>
    b.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test"));

ASP.NET Core 컨트롤러와 같은 애플리케이션 서비스는 서비스 생성자의 IDbContextFactory<TContext>에 따라 다릅니다.Application services such as ASP.NET Core controllers can then depend on IDbContextFactory<TContext> in the service constructor. 예를 들어:For example:

public class MyController
{
    private readonly IDbContextFactory<SomeDbContext> _contextFactory;

    public MyController(IDbContextFactory<SomeDbContext> contextFactory)
    {
        _contextFactory = contextFactory;
    }
}

그런 다음 필요에 따라 DbContext 인스턴스를 만들고 사용할 수 있습니다.DbContext instances can then be created and used as needed. 예를 들어:For example:

public void DoSomeThing()
{
    using (var context = _contextFactory.CreateDbContext())
    {
        // ...
    }
}

이러한 방식으로 만든 DbContext 인스턴스는 애플리케이션의 서비스 공급자에 의해 관리되지 ‘않으므로’ 애플리케이션에서 삭제해야 합니다.Note that the DbContext instances created in this way are not managed by the application's service provider and therefore must be disposed by the application. 이러한 분리는 IDbContextFactory를 사용하는 것이 좋지만 다른 시나리오에서도 유용할 수 있는 Blazor 애플리케이션에 매우 유용합니다.This decoupling is very useful for Blazor applications, where using IDbContextFactory is recommended, but may also be useful in other scenarios.

DbContext 인스턴스는 AddPooledDbContextFactory를 호출하여 풀링할 수 있습니다.DbContext instances can be pooled by calling AddPooledDbContextFactory. 이 풀링은 AddDbContextPool의 경우와 동일한 방식으로 작동하며 동일한 제한 사항이 있습니다.This pooling works the same way as for AddDbContextPool, and also has the same limitations.

설명서는 이슈 #2523에서 찾아볼 수 있습니다.Documentation is tracked by issue #2523.

DbContext 상태 재설정Reset DbContext state

EF Core 5.0에는 추적되는 모든 엔터티의 DbContext를 지우는 ChangeTracker.Clear()가 도입되었습니다.EF Core 5.0 introduces ChangeTracker.Clear() which clears the DbContext of all tracked entities. 각 작업 단위에 대해 새로운 단기 컨텍스트 인스턴스를 만드는 모범 사례를 사용할 때는 일반적으로 이 작업은 필요하지 않습니다.This should usually not be needed when using the best practice of creating a new, short-lived context instance for each unit-of-work. 그러나 DbContext 인스턴스의 상태를 재설정해야 하는 경우에는 새 Clear() 메서드를 사용하는 것이 모든 엔터티를 대량 분리하는 것보다 성능이 뛰어나고 강력합니다.However, if there is a need to reset the state of a DbContext instance, then using the new Clear() method is more performant and robust than mass-detaching all entities.

설명서는 이슈 #2524에서 찾아볼 수 있습니다.Documentation is tracked by issue #2524.

저장소 생성 기본값의 새 패턴New pattern for store-generated defaults

EF Core는 기본값 제약 조건이 있을 수 있는 열에 대해 명시적인 값을 설정할 수 있습니다.EF Core allows an explicit value to be set for a column that may also have default value constraint. EF Core에서는 Type 속성 형식의 CLR 기본값을 이에 대한 센티널로 사용합니다. 값이 CLR 기본값이 아닌 경우 삽입되고, 그렇지 않으면 데이터베이스 기본값이 사용됩니다.EF Core uses the CLR default of type property type as a sentinel for this; if the value is not the CLR default, then it is inserted, otherwise the database default is used.

이에 따라 형식에서 CLR 기본값이 적절한 센티널이 아닌(특히 bool 속성) 문제가 발생합니다.This creates problems for types where the CLR default is not a good sentinel--most notably, bool properties. 이제 EF Core 5.0에서는 이런 경우 지원 필드는 null을 허용합니다.EF Core 5.0 now allows the backing field to be nullable for cases like this. 예를 들어:For example:

public class Blog
{
    private bool? _isValid;

    public bool IsValid
    {
        get => _isValid ?? false;
        set => _isValid = value;
    }
}

지원 필드는 null을 허용하지만 공개적으로 노출되는 속성은 그렇지 않습니다.Note that the backing field is nullable, but the publicly exposed property is not. 이렇게 되면 엔터티 형식의 공개 표면에 영향을 주지 않고 센티널 값이 null일 수 있습니다.This allows the sentinel value to be null without impacting the public surface of the entity type. 이 경우 IsValid가 설정되지 않으면 지원 필드가 null로 유지되기 때문에 데이터베이스 기본값이 사용됩니다.In this case, if the IsValid is never set, then the database default will be used since the backing field remains null. true 또는 false가 설정되어 있으면 이 값은 데이터베이스에 명시적으로 저장됩니다.If either true or false are set, then this value is saved explicitly to the database.

설명서는 이슈 #2525에서 찾아볼 수 있습니다.Documentation is tracked by issue #2525.

Cosmos 파티션 키Cosmos partition keys

EF Core를 사용하면 Cosmos 파티션 키가 EF 모델에 포함됩니다.EF Core allows the Cosmos partition key is included in the EF model. 예를 들어:For example:

modelBuilder.Entity<Customer>().HasPartitionKey(b => b.AlternateKey)

미리 보기 7부터 이 파티션 키는 엔터티 형식의 PK에 포함되며 일부 쿼리에서는 성능을 향상하는 데 사용됩니다.Starting with preview 7, the partition key is included in the entity type's PK and is used to improved performance in some queries.

설명서는 이슈 #2471에서 찾아볼 수 있습니다.Documentation is tracked by issue #2471.

Cosmos 구성Cosmos configuration

EF Core 5.0은 Cosmos 및 Cosmos 연결의 구성을 향상합니다.EF Core 5.0 improves configuration of Cosmos and Cosmos connections.

이전에는 EF Core에서 Cosmos 데이터베이스에 연결할 때 엔드포인트와 키를 명시적으로 지정해야 했습니다.Previously, EF Core required the end-point and key to be specified explicitly when connecting to a Cosmos database. EF Core 5.0에서는 연결 문자열을 대신 사용할 수 있습니다.EF Core 5.0 allows use of a connection string instead. 또한 EF Core 5.0에서는 WebProxy 인스턴스를 명시적으로 설정할 수 있습니다.In addition, EF Core 5.0 allows the WebProxy instance to be explicitly set. 예를 들어:For example:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseCosmos("my-cosmos-connection-string", "MyDb",
            cosmosOptionsBuilder =>
            {
                cosmosOptionsBuilder.WebProxy(myProxyInstance);
            });

다른 많은 시간 제한 값, 한도 등도 구성할 수 있습니다.Many other timeout values, limits, etc. can now also be configured. 예를 들어:For example:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseCosmos("my-cosmos-connection-string", "MyDb",
            cosmosOptionsBuilder =>
            {
                cosmosOptionsBuilder.LimitToEndpoint();
                cosmosOptionsBuilder.RequestTimeout(requestTimeout);
                cosmosOptionsBuilder.OpenTcpConnectionTimeout(timeout);
                cosmosOptionsBuilder.IdleTcpConnectionTimeout(timeout);
                cosmosOptionsBuilder.GatewayModeMaxConnectionLimit(connectionLimit);
                cosmosOptionsBuilder.MaxTcpConnectionsPerEndpoint(connectionLimit);
                cosmosOptionsBuilder.MaxRequestsPerTcpConnection(requestLimit);
            });

마지막으로, 이제 기본 연결 모드가 ConnectionMode.Gateway이며 일반적으로 호환성이 더 높습니다.Finally, the default connection mode is now ConnectionMode.Gateway, which is generally more compatible.

설명서는 이슈 #2471에서 찾아볼 수 있습니다.Documentation is tracked by issue #2471.

DbContext 스캐폴딩이 이제 단수화됨Scaffold-DbContext now singularizes

이전에는 기존 데이터베이스에서 DbContext를 스캐폴딩할 때 EF Core에서 데이터베이스의 테이블 이름과 일치하는 엔터티 형식 이름을 생성합니다.Previously when scaffolding a DbContext from an existing database, EF Core will create entity type names that match the table names in the database. 예를 들어 테이블 이름이 PeopleAddresses이면 엔터티 형식 이름은 PeopleAddresses입니다.For example, tables People and Addresses resulted in entity types named People and Addresses.

이전 릴리스에서는 이 동작을 복수화 서비스 등록을 통해 구성할 수 있었습니다.In previous releases, this behavior was configurable through registration of a pluralization service. 이제 EF Core 5.0에서는 Humanizer 패키지가 기본 복수화 서비스로 사용됩니다.Now in EF Core 5.0, the Humanizer package is used as a default pluralization service. 즉, 테이블 이름이 PeopleAddresses이면 이제 PersonAddress라는 엔터티 형식으로 리버스 엔지니어링됩니다.This means tables People and Addresses will now be reverse engineered to entity types named Person and Address.

저장점Savepoints

이제 EF Core는 여러 작업을 실행하는 트랜잭션을 보다 효율적으로 제어할 수 있도록 저장점을 지원합니다.EF Core now supports savepoints for greater control over transactions that execute multiple operations.

저장점은 수동으로 생성, 릴리스 및 롤백할 수 있습니다.Savepoints can be manually created, released, and rolled back. 예를 들어:For example:

context.Database.CreateSavepoint("MySavePoint");

또한 EF Core는 SaveChanges 실행이 실패하는 경우 이제 마지막 저장점으로 롤백됩니다.In addition, EF Core will now roll back to the last savepoint when executing SaveChanges fails. 이렇게 하면 전체 트랜잭션을 다시 시도하지 않고 SaveChanges를 다시 시도할 수 있습니다.This allows SaveChanges to be re-tried without re-trying the entire transaction.

설명서는 이슈 #2429에서 찾아볼 수 있습니다.Documentation is tracked by issue #2429.

미리 보기 6Preview 6

EF Core 3.0부터 EF Core에서는 항상 각 LINQ 쿼리에 단일 SQL 쿼리를 생성합니다.Starting with EF Core 3.0, EF Core always generates a single SQL query for each LINQ query. 따라서 사용 중인 트랜잭션 모드의 제약 조건 내에서 반환된 데이터의 일관성이 보장됩니다.This ensures consistency of the data returned within the constraints of the transaction mode in use. 그러나 쿼리에서 Include 또는 프로젝션을 사용하여 여러 관련 컬렉션을 다시 가져오는 경우 매우 느려질 수 있습니다.However, this can become very slow when the query uses Include or a projection to bring back multiple related collections.

이제 EF Core 5.0에서는 관련 컬렉션을 포함해 단일 LINQ 쿼리를 여러 SQL 쿼리로 분할할 수 있습니다.EF Core 5.0 now allows a single LINQ query including related collections to be split into multiple SQL queries. 이렇게 하면 성능을 크게 향상할 수 있지만 두 쿼리 사이에 데이터가 변경되는 경우 반환되는 결과가 일치하지 않을 수 있습니다.This can significantly improve performance, but can result in inconsistency in the results returned if the data changes between the two queries. 직렬화 가능 트랜잭션 또는 스냅샷 트랜잭션을 사용하면 분할 쿼리에서 이 문제를 완화하고 일관성을 얻을 수 있지만 다른 성능 비용과 동작 차이를 초래할 수 있습니다.Serializable or snapshot transactions can be used to mitigate this and achieve consistency with split queries, but that may bring other performance costs and behavioral difference.

Include를 사용한 분할 쿼리Split queries with Include

예를 들어 Include를 사용하여 두 가지 수준의 관련 컬렉션을 가져오는 쿼리를 살펴보겠습니다.For example, consider a query that pulls in two levels of related collections using Include:

var artists = context.Artists
    .Include(e => e.Albums).ThenInclude(e => e.Tags)
    .ToList();

기본적으로 EF Core에서는 SQLite 공급자를 사용할 때 다음 SQL을 생성합니다.By default, EF Core will generate the following SQL when using the SQLite provider:

SELECT "a"."Id", "a"."Name", "t0"."Id", "t0"."ArtistId", "t0"."Title", "t0"."Id0", "t0"."AlbumId", "t0"."Name"
FROM "Artists" AS "a"
LEFT JOIN (
    SELECT "a0"."Id", "a0"."ArtistId", "a0"."Title", "t"."Id" AS "Id0", "t"."AlbumId", "t"."Name"
    FROM "Album" AS "a0"
    LEFT JOIN "Tag" AS "t" ON "a0"."Id" = "t"."AlbumId"
) AS "t0" ON "a"."Id" = "t0"."ArtistId"
ORDER BY "a"."Id", "t0"."Id", "t0"."Id0"

AsSplitQuery API를 사용하여 이 동작을 변경할 수 있습니다.The new AsSplitQuery API can be used to change this behavior. 예를 들어:For example:

var artists = context.Artists
    .AsSplitQuery()
    .Include(e => e.Albums).ThenInclude(e => e.Tags)
    .ToList();

AsSplitQuery는 모든 관계형 데이터베이스 공급자에 제공되며 AsNoTracking과 같이 쿼리에서 어디에나 사용할 수 있습니다.AsSplitQuery is available for all relational database providers and can be used anywhere in the query, just like AsNoTracking. 이제 EF Core는 다음과 같은 세 가지 SQL 쿼리를 생성합니다.EF Core will now generate the following three SQL queries:

SELECT "a"."Id", "a"."Name"
FROM "Artists" AS "a"
ORDER BY "a"."Id"

SELECT "a0"."Id", "a0"."ArtistId", "a0"."Title", "a"."Id"
FROM "Artists" AS "a"
INNER JOIN "Album" AS "a0" ON "a"."Id" = "a0"."ArtistId"
ORDER BY "a"."Id", "a0"."Id"

SELECT "t"."Id", "t"."AlbumId", "t"."Name", "a"."Id", "a0"."Id"
FROM "Artists" AS "a"
INNER JOIN "Album" AS "a0" ON "a"."Id" = "a0"."ArtistId"
INNER JOIN "Tag" AS "t" ON "a0"."Id" = "t"."AlbumId"
ORDER BY "a"."Id", "a0"."Id"

쿼리 루트의 모든 작업이 지원됩니다.All operations on the query root are supported. 여기에는 OrderBy/Skip/Take, Join 작업, FirstOrDefault 및 유사한 단일 결과 선택 작업이 포함됩니다.This includes OrderBy/Skip/Take, Join operations, FirstOrDefault and similar single result selecting operations.

OrderBy/Skip/Take를 사용한 필터링된 Include는 미리 보기 6에서 지원되지 않지만 일별 빌드에서 제공되며 미리 보기 7에 포함될 예정입니다.Note that filtered Includes with OrderBy/Skip/Take are not supported in preview 6, but are available in the daily builds and will be included in preview 7.

컬렉션 프로젝션을 사용한 분할 쿼리Split queries with collection projections

컬렉션이 프로젝션에 로드된 경우에도 AsSplitQuery를 사용할 수 있습니다.AsSplitQuery can also be used when collections are loaded in projections. 예를 들어:For example:

context.Artists
    .AsSplitQuery()
    .Select(e => new
    {
        Artist = e,
        Albums = e.Albums,
    }).ToList();

이 LINQ 쿼리는 SQLite 공급자를 사용하는 경우 다음과 같은 두 가지 SQL 쿼리를 생성합니다.This LINQ query generates the following two SQL queries when using the SQLite provider:

SELECT "a"."Id", "a"."Name"
FROM "Artists" AS "a"
ORDER BY "a"."Id"

SELECT "a0"."Id", "a0"."ArtistId", "a0"."Title", "a"."Id"
FROM "Artists" AS "a"
INNER JOIN "Album" AS "a0" ON "a"."Id" = "a0"."ArtistId"
ORDER BY "a"."Id"

컬렉션의 구체화만 지원됩니다.Note that only materialization of the collection is supported. 위의 경우에서 e.Albums 이후 컴퍼지션은 분할 쿼리가 되지 않습니다.Any composition after e.Albums in above case won't result in a split query. 이 영역의 개선 사항은 #21234에서 찾아볼 수 있습니다.Improvements in this area are tracked by #21234.

IndexAttributeIndexAttribute

새 IndexAttribute를 엔터티 형식에 배치하여 단일 열의 인덱스를 지정할 수 있습니다.The new IndexAttribute can be placed on an entity type to specify an index for a single column. 예를 들어:For example:

[Index(nameof(FullName), IsUnique = true)]
public class User
{
    public int Id { get; set; }

    [MaxLength(128)]
    public string FullName { get; set; }
}

그러면 SQL Server의 경우 마이그레이션이 다음 SQL을 생성합니다.For SQL Server, Migrations will then generate the following SQL:

CREATE UNIQUE INDEX [IX_Users_FullName]
    ON [Users] ([FullName])
    WHERE [FullName] IS NOT NULL;

IndexAttribute를 사용하여 여러 열에 걸친 인덱스를 지정할 수도 있습니다.IndexAttribute can also be used to specify an index spanning multiple columns. 예를 들면 다음과 같습니다.For example:

[Index(nameof(FirstName), nameof(LastName), IsUnique = true)]
public class User
{
    public int Id { get; set; }

    [MaxLength(64)]
    public string FirstName { get; set; }

    [MaxLength(64)]
    public string LastName { get; set; }
}

SQL Server의 경우 결과는 다음과 같습니다.For SQL Server, this results in:

CREATE UNIQUE INDEX [IX_Users_FirstName_LastName]
    ON [Users] ([FirstName], [LastName])
    WHERE [FirstName] IS NOT NULL AND [LastName] IS NOT NULL;

설명서는 이슈 #2407에서 찾아볼 수 있습니다.Documentation is tracked by issue #2407.

향상된 쿼리 변환 예외Improved query translation exceptions

쿼리 변환이 실패할 때 생성되는 예외 메시지를 계속 개선하고 있습니다.We are continuing to improve the exception messages generated when query translation fails. 예를 들어 아래 쿼리는 매핑되지 않은 속성 IsSigned를 사용합니다.For example, this query uses the unmapped property IsSigned:

var artists = context.Artists.Where(e => e.IsSigned).ToList();

EF Core는 IsSigned가 매핑되지 않았으므로 변환이 실패했음을 나타내는 다음 예외를 throw합니다.EF Core will throw the following exception indicating that translation failed because IsSigned is not mapped:

처리되지 않은 예외가 발생했습니다.Unhandled exception. System.InvalidOperationException: LINQ 식 ‘DbSet() .Where(a => a.IsSigned)’를 변환할 수 없습니다.System.InvalidOperationException: The LINQ expression 'DbSet() .Where(a => a.IsSigned)' could not be translated. 추가 정보: 엔터티 형식 ‘Artist’에서 ‘IsSigned’ 멤버를 변환하지 못했습니다.Additional information: Translation of member 'IsSigned' on entity type 'Artist' failed. 지정된 멤버가 매핑되지 않았을 수 있습니다.Possibly the specified member is not mapped. 변환할 수 있는 형식으로 쿼리를 다시 작성하거나 AsEnumerable(), AsAsyncEnumerable(), ToList() 또는 ToListAsync()에 대한 호출을 삽입하여 명시적으로 클라이언트 평가로 전환하세요.Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2101038 를 참조하세요.See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

마찬가지로, 문화권 종속 의미 체계를 사용하여 문자열 비교를 변환하려고 할 때 이제 더 나은 예외 메시지가 생성됩니다.Similarly, better exception messages are now generated when attempting to translate string comparisons with culture-dependent semantics. 예를 들어 아래 쿼리는 StringComparison.CurrentCulture를 사용하려고 합니다.For example, this query attempts to use StringComparison.CurrentCulture:

var artists = context.Artists
    .Where(e => e.Name.Equals("The Unicorns", StringComparison.CurrentCulture))
    .ToList();

이제 EF Core는 다음 예외를 throw합니다.EF Core will now throw the following exception:

처리되지 않은 예외가 발생했습니다.Unhandled exception. System.InvalidOperationException: LINQ 식 ‘DbSet() .Where(a => a.Name.Equals( value: “The Unicorns”, comparisonType: CurrentCulture))’를 변환할 수 없습니다.System.InvalidOperationException: The LINQ expression 'DbSet() .Where(a => a.Name.Equals( value: "The Unicorns", comparisonType: CurrentCulture))' could not be translated. 추가 정보: ‘StringComparison’ 인수를 사용하는 ‘string.Equals’ 메서드의 변환은 지원되지 않습니다.Additional information: Translation of 'string.Equals' method which takes 'StringComparison' argument is not supported. 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2129535 를 참조하세요.See https://go.microsoft.com/fwlink/?linkid=2129535 for more information. 변환할 수 있는 형식으로 쿼리를 다시 작성하거나 AsEnumerable(), AsAsyncEnumerable(), ToList() 또는 ToListAsync()에 대한 호출을 삽입하여 명시적으로 클라이언트 평가로 전환하세요.Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=2101038 를 참조하세요.See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

트랜잭션 ID 지정Specify transaction ID

이 기능은 커뮤니티에서 @Marusyk가 제공했습니다.This feature was contributed from the community by @Marusyk. 기여해 주셔서 감사합니다.Many thanks for the contribution!

EF Core는 호출 간 트랜잭션의 상관 관계에 필요한 트랜잭션 ID를 노출합니다.EF Core exposes a transaction ID for correlation of transactions across calls. 일반적으로 이 ID는 트랜잭션이 시작될 때 EF Core에서 설정합니다.This ID typically set by EF Core when a transaction is started. 대신 애플리케이션에서 트랜잭션을 시작하는 경우 이 기능을 통해 애플리케이션에서 트랜잭션 ID를 명시적으로 설정할 수 있으므로 트랜잭션을 사용하는 어디서나 상관 관계를 올바로 지정합니다.If the application starts the transaction instead, then this feature allows the application to explicitly set the transaction ID so it is correlated correctly everywhere it is used. 예를 들어:For example:

using (context.Database.UseTransaction(myTransaction, myId))
{
   ...
}

IPAddress 매핑IPAddress mapping

이 기능은 커뮤니티에서 @ralmsdeveloper가 제공했습니다.This feature was contributed from the community by @ralmsdeveloper. 기여해 주셔서 감사합니다.Many thanks for the contribution!

이제 표준 .NET IPAddress 클래스가 기본 지원이 아직 없는 데이터베이스의 문자열 열에 자동으로 매핑됩니다.The standard .NET IPAddress class is now automatically mapped to a string column for databases that do not already have native support. 예를 들어 다음 엔터티 형식을 매핑한다고 가정해 보겠습니다.For example, consider mapping this entity type:

public class Host
{
    public int Id { get; set; }
    public IPAddress Address { get; set; }
}

SQL Server에서는 이 경우 마이그레이션이 다음 테이블을 생성합니다.On SQL Server, this will result in Migrations creating the following table:

CREATE TABLE [Host] (
    [Id] int NOT NULL,
    [Address] nvarchar(45) NULL,
    CONSTRAINT [PK_Host] PRIMARY KEY ([Id]));

그런 다음 일반적인 방법으로 엔터티를 추가할 수 있습니다.Entities can then be added in the normal way:

context.AddRange(
    new Host { Address = IPAddress.Parse("127.0.0.1")},
    new Host { Address = IPAddress.Parse("0000:0000:0000:0000:0000:0000:0000:0001")});

그리고 결과 SQL은 정규화된 IPv4 또는 IPv6 주소를 삽입합니다.And the resulting SQL will insert the normalized IPv4 or IPv6 address:

Executed DbCommand (14ms) [Parameters=[@p0='1', @p1='127.0.0.1' (Size = 45), @p2='2', @p3='::1' (Size = 45)], CommandType='Text', CommandTimeout='30']
      SET NOCOUNT ON;
      INSERT INTO [Host] ([Id], [Address])
      VALUES (@p0, @p1), (@p2, @p3);

스캐폴딩 시 OnConfiguring 제외Exclude OnConfiguring when scaffolding

기존 데이터베이스에서 DbContext를 스캐폴드하는 경우 기본적으로 EF Core는 컨텍스트를 즉시 사용할 수 있도록 연결 문자열을 사용하여 OnConfiguring 오버로드를 생성합니다.When a DbContext is scaffolded from an existing database, EF Core by default creates an OnConfiguring overload with a connection string so that the context is immediately usable. 그러나 OnConfiguring을 사용하는 partial 클래스가 이미 있거나 컨텍스트를 다른 방식으로 구성하는 경우에는 이 기능이 유용하지 않습니다.However, this is not useful if you already have a partial class with OnConfiguring, or if you are configuring the context some other way.

이 문제를 해결하기 위해 이제 스캐폴딩 명령에서 OnConfiguring의 생성을 생략하도록 지시할 수 있습니다.To address this, the scaffolding commands can now be instructed to omit generation of OnConfiguring. 예를 들어:For example:

dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer --no-onconfiguring

또는 패키지 관리자 콘솔에서는 다음과 같습니다.Or in the Package Manager Console:

Scaffold-DbContext 'Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook' Microsoft.EntityFrameworkCore.SqlServer -NoOnConfiguring

명명된 연결 문자열 및 사용자 비밀과 같은 보안 스토리지를 사용하는 것이 좋습니다.Note that we recommend using a named connection string and secure storage like User Secrets.

문자열에서의 FirstOrDefault 변환Translations for FirstOrDefault on strings

이 기능은 커뮤니티에서 @dvoreckyaa가 제공했습니다.This feature was contributed from the community by @dvoreckyaa. 기여해 주셔서 감사합니다.Many thanks for the contribution!

문자열의 문자를 위한 FirstOrDefault 및 유사한 연산자가 이제 변환됩니다.FirstOrDefault and similar operators for characters in strings are now translated. 예를 들어 다음 LINQ 쿼리는For example, this LINQ query:

context.Customers.Where(c => c.ContactName.FirstOrDefault() == 'A').ToList();

SQL Server를 사용하는 경우 다음 SQL로 변환됩니다.Will be translated to the following SQL when using SQL Server:

SELECT [c].[Id], [c].[ContactName]
FROM [Customer] AS [c]
WHERE SUBSTRING([c].[ContactName], 1, 1) = N'A'

Case 블록 단순화Simplify case blocks

이제 EF Core는 CASE 블록으로 더 나은 쿼리를 생성합니다.EF Core now generates better queries with CASE blocks. 예를 들어 다음 LINQ 쿼리는For example, this LINQ query:

context.Weapons
    .OrderBy(w => w.Name.CompareTo("Marcus' Lancer") == 0)
    .ThenBy(w => w.Id)

SQL Server에서 공식적으로 다음으로 변환되었습니다.Was on SQL Server formally translated to:

SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId]
FROM [Weapons] AS [w]
ORDER BY CASE
    WHEN (CASE
        WHEN [w].[Name] = N'Marcus'' Lancer' THEN 0
        WHEN [w].[Name] > N'Marcus'' Lancer' THEN 1
        WHEN [w].[Name] < N'Marcus'' Lancer' THEN -1
    END = 0) AND CASE
        WHEN [w].[Name] = N'Marcus'' Lancer' THEN 0
        WHEN [w].[Name] > N'Marcus'' Lancer' THEN 1
        WHEN [w].[Name] < N'Marcus'' Lancer' THEN -1
    END IS NOT NULL THEN CAST(1 AS bit)
    ELSE CAST(0 AS bit)
END, [w].[Id]");

하지만 이제는 다음으로 변환됩니다.But is now translated to:

SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId]
FROM [Weapons] AS [w]
ORDER BY CASE
    WHEN ([w].[Name] = N'Marcus'' Lancer') AND [w].[Name] IS NOT NULL THEN CAST(1 AS bit)
    ELSE CAST(0 AS bit)
END, [w].[Id]");

Preview 5Preview 5

데이터베이스 데이터 정렬Database collations

이제 EF 모델에서 데이터베이스의 기본 데이터 정렬을 지정할 수 있습니다.The default collation for a database can now be specified in the EF model. 지정된 기본 데이터 정렬은 생성된 마이그레이션으로 전달되어 데이터베이스가 생성될 때 데이터 정렬을 설정합니다.This will flow through to generated migrations to set the collation when the database is created. 예를 들어:For example:

modelBuilder.UseCollation("German_PhoneBook_CI_AS");

그런 다음, 마이그레이션은 다음을 생성하여 SQL Server에서 데이터베이스를 만듭니다.Migrations then generates the following to create the database on SQL Server:

CREATE DATABASE [Test]
COLLATE German_PhoneBook_CI_AS;

특정 데이터베이스 열에 사용할 데이터 정렬을 지정할 수도 있습니다.The collation to use for specific database columns can also be specified. 예를 들어:For example:

 modelBuilder
     .Entity<User>()
     .Property(e => e.Name)
     .UseCollation("German_PhoneBook_CI_AS");

마이그레이션을 사용하지 않는 사용자의 경우 이제 DbContext를 스캐폴드할 때 데이터 정렬이 데이터베이스에서 리버스 엔지니어링됩니다.For those not using migrations, collations are now reverse-engineered from the database when scaffolding a DbContext.

마지막으로 EF.Functions.Collate()는 다른 데이터 정렬을 사용하는 임시 쿼리를 허용합니다.Finally, the EF.Functions.Collate() allows for ad-hoc queries using different collations. 예를 들어:For example:

context.Users.Single(e => EF.Functions.Collate(e.Name, "French_CI_AS") == "Jean-Michel Jarre");

이 메서드는 SQL Server에 대해 다음 쿼리를 생성합니다.This will generate the following query for SQL Server:

SELECT TOP(2) [u].[Id], [u].[Name]
FROM [Users] AS [u]
WHERE [u].[Name] COLLATE French_CI_AS = N'Jean-Michel Jarre'

임시 데이터 정렬은 데이터베이스 성능에 부정적인 영향을 줄 수 있으므로 주의해서 사용해야 합니다.Note that ad-hoc collations should be used with care as they can negatively impact database performance.

설명서는 이슈 #2273에서 찾아볼 수 있습니다.Documentation is tracked by issue #2273.

IDesignTimeDbContextFactory로 인수 전달Flow arguments into IDesignTimeDbContextFactory

이제 인수는 명령줄에서 IDesignTimeDbContextFactoryCreateDbContext 메서드로 전달됩니다.Arguments are now flowed from the command line into the CreateDbContext method of IDesignTimeDbContextFactory. 예를 들어 개발 빌드임을 나타내기 위해 명령줄에서 사용자 지정 인수(예: dev)를 전달할 수 있습니다.For example, to indicate this is a dev build, a custom argument (e.g. dev) can be passed on the command line:

dotnet ef migrations add two --verbose --dev

그런 다음, 이 인수는 팩터리로 전달되며, 여기에서 컨텍스트를 만들고 초기화하는 방법을 제어하는 데 사용할 수 있습니다.This argument will then flow into the factory, where it can be used to control how the context is created and initialized. 예를 들어:For example:

public class MyDbContextFactory : IDesignTimeDbContextFactory<SomeDbContext>
{
    public SomeDbContext CreateDbContext(string[] args)
        => new SomeDbContext(args.Contains("--dev"));
}

설명서는 이슈 #2419에서 찾아볼 수 있습니다.Documentation is tracked by issue #2419.

ID 확인이 포함된 비 추적 쿼리No-tracking queries with identity resolution

이제 ID 확인을 수행하도록 비 추적 쿼리를 구성할 수 있습니다.No-tracking queries can now be configured to perform identity resolution. 예를 들어 다음 쿼리는 각 Blog의 기본 키가 동일한 경우에도 각 Post의 새 Blog 인스턴스를 만듭니다.For example, the following query will create a new Blog instance for each Post, even if each Blog has the same primary key.

context.Posts.AsNoTracking().Include(e => e.Blog).ToList();

그러나 일반적으로 약간 더 느리고 항상 메모리를 더 많이 사용하더라도 단일 Blog 인스턴스만 생성되도록 이 쿼리를 변경할 수 있습니다.However, at the expense of usually being slightly slower and always using more memory, this query can be changed to ensure only a single Blog instance is created:

context.Posts.AsNoTracking().PerformIdentityResolution().Include(e => e.Blog).ToList();

모든 추적 쿼리가 이미 이 동작을 나타내므로 이 기능은 비 추적 쿼리에만 유용합니다.Note that this is only useful for no-tracking queries since all tracking queries already exhibit this behavior. 또한 API 검토 후에 PerformIdentityResolution 구문이 변경됩니다.Also, following API review, the PerformIdentityResolution syntax will be changed. #19877을 참조하세요.See #19877.

설명서는 이슈 #1895에서 찾아볼 수 있습니다.Documentation is tracked by issue #1895.

저장된(지속형) 계산 열Stored (persisted) computed columns

대부분의 데이터베이스에서는 계산 후에 계산 열 값을 저장할 수 있습니다.Most databases allow computed column values to be stored after computation. 이렇게 하면 디스크 공간을 차지하지만 계산 열은 값이 검색될 때마다 계산되는 것이 아니라 업데이트 시 한 번만 계산됩니다.While this takes up disk space, the computed column is calculated only once on update, instead of each time its value is retrieved. 이를 통해 일부 데이터베이스에서 열을 인덱싱할 수도 있습니다.This also allows the column to be indexed for some databases.

EF Core 5.0에서는 계산 열을 저장됨으로 구성할 수 있습니다.EF Core 5.0 allows computed columns to be configured as stored. 예를 들어:For example:

modelBuilder
    .Entity<User>()
    .Property(e => e.SomethingComputed)
    .HasComputedColumnSql("my sql", stored: true);

SQLite 계산 열SQLite computed columns

이제 EF Core는 SQLite 데이터베이스에서 계산 열을 지원합니다.EF Core now supports computed columns in SQLite databases.

Preview 4Preview 4

모델의 데이터베이스 전체 자릿수 및 소수 자릿수 구성Configure database precision/scale in model

이제 모델 작성기를 사용하여 속성의 전체 자릿수와 소수 자릿수를 지정할 수 있습니다.Precision and scale for a property can now be specified using the model builder. 예를 들어:For example:

modelBuilder
    .Entity<Blog>()
    .Property(b => b.Numeric)
    .HasPrecision(16, 4);

전체 자릿수와 소수 자릿수는 기존 방법과 같이 "decimal(16,4)"와 같은 전체 데이터 형식을 통해서도 설정할 수 있습니다.Precision and scale can still be set via the full database type, such as "decimal(16,4)".

설명서는 문제 #527에서 찾아볼 수 있습니다.Documentation is tracked by issue #527.

SQL Server 인덱스 채우기 비율 지정Specify SQL Server index fill factor

이제 SQL Server에서 인덱스를 만들 때 채우기 비율을 지정할 수 있습니다.The fill factor can now be specified when creating an index on SQL Server. 예를 들어:For example:

modelBuilder
    .Entity<Customer>()
    .HasIndex(e => e.Name)
    .HasFillFactor(90);

Preview 3Preview 3

필터링 적용 포함Filtered Include

Include 메서드 행은 포함되는 엔터티의 필터링을 지원합니다.The Include method now supports filtering of the entities included. 예를 들어:For example:

var blogs = context.Blogs
    .Include(e => e.Posts.Where(p => p.Title.Contains("Cheese")))
    .ToList();

이 쿼리는 게시물 제목에 “Cheese”가 포함된 경우에만 각 관련 게시물과 함께 블로그를 반환합니다.This query will return blogs together with each associated post, but only when the post title contains "Cheese".

Skip과 Take를 사용하여 포함되는 엔터티 수를 줄일 수도 있습니다.Skip and Take can also be used to reduce the number of included entities. 예를 들어:For example:

var blogs = context.Blogs
    .Include(e => e.Posts.OrderByDescending(post => post.Title).Take(5)))
    .ToList();

이 쿼리는 각 블로그에 5개 이하의 게시물이 포함된 블로그를 반환합니다.This query will return blogs with at most five posts included on each blog.

자세한 내용은 Include 설명서를 참조하세요.See the Include documentation for full details.

탐색 속성용 새 ModelBuilder APINew ModelBuilder API for navigation properties

탐색 속성은 관계를 정의할 때 주로 구성됩니다.Navigation properties are primarily configured when defining relationships. 그러나 탐색 속성에 추가 구성이 필요한 경우에는 새 Navigation 메서드를 사용할 수 있습니다.However, the new Navigation method can be used in the cases where navigation properties need additional configuration. 예를 들어 규칙을 통해 필드를 찾을 수 없는 경우 탐색 지원 필드를 설정하려면 다음 코드를 사용합니다.For example, to set a backing field for the navigation when the field would not be found by convention:

modelBuilder.Entity<Blog>().Navigation(e => e.Posts).HasField("_myposts");

Navigation API는 관계 구성을 바꾸지 않습니다.Note that the Navigation API does not replace relationship configuration. 대신, 이미 검색되거나 정의된 관계에 탐색 속성을 추가로 구성할 수 있습니다.Instead it allows additional configuration of navigation properties in already discovered or defined relationships.

탐색 속성 구성 설명서를 참조하세요.See the Configuring Navigation Properties documentation.

네임스페이스 및 연결 문자열의 새 명령줄 매개 변수New command-line parameters for namespaces and connection strings

이제 마이그레이션 및 스캐폴딩을 통해 명령줄에서 네임스페이스를 지정할 수 있습니다.Migrations and scaffolding now allow namespaces to be specified on the command line. 예를 들어 컨텍스트와 모델 클래스를 다른 네임스페이스에 배치하여 데이터베이스를 리버스 엔지니어링하려면 다음 코드를 사용합니다.For example, to reverse engineer a database putting the context and model classes in different namespaces:

dotnet ef dbcontext scaffold "connection string" Microsoft.EntityFrameworkCore.SqlServer --context-namespace "My.Context" --namespace "My.Model"

자세한 내용은 마이그레이션리버스 엔지니어링 설명서를 참조하세요.See the Migrations and Reverse Engineering documentation for full details.


이제 연결 문자열을 database-update 명령에 전달할 수도 있습니다.Also, a connection string can now be passed to the database-update command:

dotnet ef database update --connection "connection string"

자세한 내용은 도구 설명서를 참조하세요.See the Tools documentation for full details.

VS 패키지 관리자 콘솔에서 사용되는 PowerShell 명령에도 동일한 매개 변수가 추가되었습니다.Equivalent parameters have also been added to the PowerShell commands used in the VS Package Manager Console.

EnableDetailedErrors 반환됨EnableDetailedErrors has returned

성능상의 이유로 EF는 데이터베이스에서 값을 읽을 때 추가 null 검사를 수행하지 않습니다.For performance reasons, EF doesn't do additional null-checks when reading values from the database. 이로 인해 예기치 않은 null을 발견할 때 근본 원인을 찾기 어려운 예외가 발생할 수 있습니다.This can result in exceptions that are hard to root-cause when an unexpected null is encountered.

EnableDetailedErrors를 사용하면 약간의 성능 오버헤드는 있지만 해당 오류를 근본 원인으로 추적하기 쉽도록 쿼리에 null 검사가 추가됩니다.Using EnableDetailedErrors will add extra null checking to queries such that, for a small performance overhead, these errors are easier to trace back to a root cause.

예를 들어:For example:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .EnableDetailedErrors()
        .EnableSensitiveDataLogging() // Often also useful with EnableDetailedErrors
        .UseSqlServer(Your.SqlServerConnectionString);

설명서는 문제 #955에서 찾아볼 수 있습니다.Documentation is tracked by issue #955.

Cosmos 파티션 키Cosmos partition keys

이제 지정된 쿼리에 사용할 파티션 키를 해당 쿼리에서 지정할 수 있습니다.The partition key to use for a given query can now be specified in the query. 예를 들어:For example:

await context.Set<Customer>()
             .WithPartitionKey(myPartitionKey)
             .FirstAsync();

설명서는 문제 #2199에서 찾아볼 수 있습니다.Documentation is tracked by issue #2199.

SQL Server DATALENGTH 함수 지원Support for the SQL Server DATALENGTH function

EF.Functions.DataLength 메서드를 사용하여 액세스할 수 있습니다.This can be accessed using the new EF.Functions.DataLength method. 예를 들어:For example:

var count = context.Orders.Count(c => 100 < EF.Functions.DataLength(c.OrderDate));

Preview 2Preview 2

C# 특성을 사용하여 속성 지원 필드 지정Use a C# attribute to specify a property backing field

이제 C# 특성을 사용하여 속성의 지원 필드를 지정할 수 있습니다.A C# attribute can now be used to specify the backing field for a property. 이 특성을 사용하면 EF Core에서는 지원 필드를 자동으로 찾을 수 없는 경우에도 일반적으로 발생하는 지원 필드에 대해 쓰기 및 읽기를 계속 수행할 수 있습니다.This attribute allows EF Core to still write to and read from the backing field as would normally happen, even when the backing field cannot be found automatically. 예를 들어:For example:

public class Blog
{
    private string _mainTitle;

    public int Id { get; set; }

    [BackingField(nameof(_mainTitle))]
    public string Title
    {
        get => _mainTitle;
        set => _mainTitle = value;
    }
}

설명서는 문제 #2230에서 찾아볼 수 있습니다.Documentation is tracked by issue #2230.

완전한 판별자 매핑Complete discriminator mapping

EF Core는 상속 계층 구조의 TPH 매핑을 위해 판별자 열을 사용합니다.EF Core uses a discriminator column for TPH mapping of an inheritance hierarchy. EF Core에서 판별자의 가능한 모든 값을 알고 있는 경우 몇 가지 성능 개선이 가능해집니다.Some performance enhancements are possible so long as EF Core knows all possible values for the discriminator. EF Core 5.0은 이제 이러한 향상된 기능을 구현합니다.EF Core 5.0 now implements these enhancements.

예를 들어 이전 버전의 EF Core는 항상 계층의 모든 형식을 반환하는 쿼리에 대해 이 SQL을 생성합니다.For example, previous versions of EF Core would always generate this SQL for a query returning all types in a hierarchy:

SELECT [a].[Id], [a].[Discriminator], [a].[Name]
FROM [Animal] AS [a]
WHERE [a].[Discriminator] IN (N'Animal', N'Cat', N'Dog', N'Human')

EF Core 5.0은 이제 전체 판별자 매핑이 구성될 때 다음을 생성합니다.EF Core 5.0 will now generate the following when a complete discriminator mapping is configured:

SELECT [a].[Id], [a].[Discriminator], [a].[Name]
FROM [Animal] AS [a]

미리 보기 3부터 기본 동작으로 사용됩니다.It will be the default behavior starting with preview 3.

Microsoft.Data.Sqlite의 성능 향상Performance improvements in Microsoft.Data.Sqlite

SQLIte에 대해 다음과 같은 두 가지 개선된 성능 기능이 구현되었습니다.We have made two performance improvements for SQLIte:

  • 이제 SqliteBlob 및 스트림을 사용하여 GetBytes, GetChars 및 GetTextReader를 사용한 이진 및 문자열 데이터 검색을 보다 효율적으로 진행할 수 있습니다.Retrieving binary and string data with GetBytes, GetChars, and GetTextReader is now more efficient by making use of SqliteBlob and streams.
  • 이제 SqliteConnection의 초기화가 지연됩니다.Initialization of SqliteConnection is now lazy.

이러한 향상된 기능은 ADO.NET Microsoft.Data.Sqlite 공급자에서 제공되므로 EF Core 외부의 성능도 향상됩니다.These improvements are in the ADO.NET Microsoft.Data.Sqlite provider and hence also improve performance outside of EF Core.

미리 보기 1Preview 1

간단한 로깅Simple logging

이 기능은 EF6의 Database.Log와 유사한 기능을 추가합니다.This feature adds functionality similar to Database.Log in EF6. 즉, 어떠한 외부 로깅 프레임워크도 구성할 필요 없이 EF Core에서 로그를 가져오는 간단한 방법을 제공합니다.That is, it provides a simple way to get logs from EF Core without the need to configure any kind of external logging framework.

예비 설명서는 2019년 12월 5일의 EF 주간 상태에 포함되어 있습니다.Preliminary documentation is included in the EF weekly status for December 5, 2019.

추가 설명서는 문제 #2085에서 찾아볼 수 있습니다.Additional documentation is tracked by issue #2085.

SQL을 생성하는 간단한 방법Simple way to get generated SQL

EF Core 5.0에는 EF Core가 LINQ 쿼리를 실행할 때 생성하는 SQL을 반환하는 ToQueryString 확장 메서드가 도입되었습니다.EF Core 5.0 introduces the ToQueryString extension method, which will return the SQL that EF Core will generate when executing a LINQ query.

예비 설명서는 2020년 1월 9일의 EF 주간 상태에 포함되어 있습니다.Preliminary documentation is included in the EF weekly status for January 9, 2020.

추가 설명서는 문제 #1331에서 찾아볼 수 있습니다.Additional documentation is tracked by issue #1331.

C# 특성을 사용하여 엔터티에 키가 없음을 나타냄Use a C# attribute to indicate that an entity has no key

이제 새 KeylessAttribute를 사용하는 키가 없는 것으로 엔터티 형식을 구성할 수 있습니다.An entity type can now be configured as having no key using the new KeylessAttribute. 예를 들어:For example:

[Keyless]
public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public int Zip { get; set; }
}

설명서는 문제 #2186에서 찾아볼 수 있습니다.Documentation is tracked by issue #2186.

초기화된 DbContext에서 연결 또는 연결 문자열을 변경할 수 있습니다.Connection or connection string can be changed on initialized DbContext

이제 연결 문자열 또는 연결 문자열 없이 DbContext 인스턴스를 보다 쉽게 만들 수 있습니다.It is now easier to create a DbContext instance without any connection or connection string. 또한 이제 컨텍스트 인스턴스에서 연결 또는 연결 문자열을 변경할 수 있습니다.Also, the connection or connection string can now be mutated on the context instance. 이 기능을 사용하면 동일한 컨텍스트 인스턴스를 다른 데이터베이스에 동적으로 연결할 수 있습니다.This feature allows the same context instance to dynamically connect to different databases.

설명서는 문제 #2075에서 찾아볼 수 있습니다.Documentation is tracked by issue #2075.

변경 내용 추적 프록시Change-tracking proxies

이제 EF Core에서 INotifyPropertyChangingINotifyPropertyChanged를 자동으로 구현하는 런타임 프록시를 생성할 수 있습니다.EF Core can now generate runtime proxies that automatically implement INotifyPropertyChanging and INotifyPropertyChanged. 이후 엔터티 속성의 값 변경 내용을 EF Core에 직접 보고하므로 변경 내용을 별도로 검사하지 않아도 됩니다.These then report value changes on entity properties directly to EF Core, avoiding the need to scan for changes. 그러나 프록시에는 자체적으로 제한 사항이 적용되어 있어 일부 사용자에게는 적합하지 않습니다.However, proxies come with their own set of limitations, so they are not for everyone.

설명서는 문제 #2076에서 찾아볼 수 있습니다.Documentation is tracked by issue #2076.

향상된 디버그 보기Enhanced debug views

디버그 보기를 통해 문제를 디버깅할 때 EF Core의 내부를 쉽게 확인할 수 있습니다.Debug views are an easy way to look at the internals of EF Core when debugging issues. 모델의 디버그 보기가 며칠 전에 구현되었습니다.A debug view for the Model was implemented some time ago. EF Core 5.0에서는 모델 보기를 간편하게 읽을 수 있으며, 상태 관리자에서 추적된 엔터티를 확인할 수 있는 새 디버그 보기가 추가되었습니다.For EF Core 5.0, we have made the model view easier to read and added a new debug view for tracked entities in the state manager.

예비 설명서는 2019년 12월 12일의 EF 주간 상태에 포함되어 있습니다.Preliminary documentation is included in the EF weekly status for December 12, 2019.

추가 설명서는 문제 #2086에서 찾아볼 수 있습니다.Additional documentation is tracked by issue #2086.

향상된 데이터베이스 null 의미 체계 처리Improved handling of database null semantics

관계형 데이터베이스는 일반적으로 NULL을 알 수 없는 값으로 처리하므로 다른 NULL과 같지 않습니다.Relational databases typically treat NULL as an unknown value and therefore not equal to any other NULL. 그렇지만 C#은 null을 다른 null과 동일하게 비교하는 정의된 값으로 처리합니다.While C# treats null as a defined value, which compares equal to any other null. 기본적으로 EF Core는 쿼리를 변환하여 C# null 의미 체계를 사용합니다.EF Core by default translates queries so that they use C# null semantics. EF Core 5.0은 이 변환 과정의 효율성을 크게 향상시킵니다.EF Core 5.0 greatly improves the efficiency of these translations.

설명서는 문제 #1612에서 찾아볼 수 있습니다.Documentation is tracked by issue #1612.

인덱서 속성Indexer properties

EF Core 5.0는 C# 인덱서 속성의 매핑을 지원합니다.EF Core 5.0 supports mapping of C# indexer properties. 이러한 속성을 사용하여 엔터티를 열이 속성 모음의 명명된 속성으로 매핑되는 속성 모음으로 사용할 수 있습니다.These properties allow entities to act as property bags where columns are mapped to named properties in the bag.

설명서는 문제 #2018에서 찾아볼 수 있습니다.Documentation is tracked by issue #2018.

열거형 매핑의 check 제약 조건 생성Generation of check constraints for enum mappings

이제 EF Core 5.0 마이그레이션을 통해 열거형 속성 매핑의 CHECK 제약 조건을 생성할 수 있습니다.EF Core 5.0 Migrations can now generate CHECK constraints for enum property mappings. 예를 들어:For example:

MyEnumColumn VARCHAR(10) NOT NULL CHECK (MyEnumColumn IN ('Useful', 'Useless', 'Unknown'))

설명서는 문제 #2082에서 찾아볼 수 있습니다.Documentation is tracked by issue #2082.

IsRelationalIsRelational

기존의 IsSqlServer, IsSqlite, IsInMemory 외에 새로운 IsRelational 메서드가 추가되었습니다.A new IsRelational method has been added in addition to the existing IsSqlServer, IsSqlite, and IsInMemory. 이 메서드는 DbContext가 관계형 데이터베이스 공급자를 사용 중인지를 테스트하는 데 사용할 수 있습니다.This method can be used to test if the DbContext is using any relational database provider. 예를 들어:For example:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    if (Database.IsRelational())
    {
        // Do relational-specific model configuration.
    }
}

설명서는 문제 #2185에서 찾아볼 수 있습니다.Documentation is tracked by issue #2185.

ETag를 사용한 Cosmos 낙관적 동시성Cosmos optimistic concurrency with ETags

Azure Cosmos DB 데이터베이스 공급자는 이제 ETag를 사용하여 낙관적 동시성을 지원합니다.The Azure Cosmos DB database provider now supports optimistic concurrency using ETags. OnModelCreating에서 모델 작성기를 사용하여 ETag를 구성합니다.Use the model builder in OnModelCreating to configure an ETag:

builder.Entity<Customer>().Property(c => c.ETag).IsEtagConcurrency();

그런 다음 SaveChanges에서 동시성 충돌 시 DbUpdateConcurrencyException을 throw하며, 다시 시도를 구현하기 위해 충돌을 처리할 수 있습니다.SaveChanges will then throw an DbUpdateConcurrencyException on a concurrency conflict, which can be handled to implement retries, etc.

설명서는 문제 #2099에서 찾아볼 수 있습니다.Documentation is tracked by issue #2099.

더 많은 날짜/시간 구문의 쿼리 변환Query translations for more DateTime constructs

이제 새로운 DateTime 생성을 포함하는 쿼리가 변환됩니다.Queries containing new DateTime construction are now translated.

또한 이제 다음과 같은 SQL Server 함수가 매핑됩니다.In addition, the following SQL Server functions are now mapped:

  • DateDiffWeekDateDiffWeek
  • DateFromPartsDateFromParts

예를 들어:For example:

var count = context.Orders.Count(c => date > EF.Functions.DateFromParts(DateTime.Now.Year, 12, 25));

설명서는 문제 #2079에서 찾아볼 수 있습니다.Documentation is tracked by issue #2079.

더 많은 바이트 배열 구문의 쿼리 변환Query translations for more byte array constructs

Contains, Length, SequenceEqual 등을 사용하는 쿼리는 byte[] 속성에서 이제 SQL로 변환됩니다.Queries using Contains, Length, SequenceEqual, etc. on byte[] properties are now translated to SQL.

예비 설명서는 2019년 12월 5일의 EF 주간 상태에 포함되어 있습니다.Preliminary documentation is included in the EF weekly status for December 5, 2019.

추가 설명서는 문제 #2079에서 찾아볼 수 있습니다.Additional documentation is tracked by issue #2079.

역방향 쿼리 변환Query translation for Reverse

이제 Reverse를 사용하는 쿼리가 변환됩니다.Queries using Reverse are now translated. 예를 들어:For example:

context.Employees.OrderBy(e => e.EmployeeID).Reverse()

설명서는 문제 #2079에서 찾아볼 수 있습니다.Documentation is tracked by issue #2079.

비트 연산자의 쿼리 변환Query translation for bitwise operators

비트 연산자를 사용하는 쿼리는 다음과 같이 더 다양한 경우에 변환됩니다.Queries using bitwise operators are now translated in more cases For example:

context.Orders.Where(o => ~o.OrderID == negatedId)

설명서는 문제 #2079에서 찾아볼 수 있습니다.Documentation is tracked by issue #2079.

Cosmos 문자열의 쿼리 변환Query translation for strings on Cosmos

이제 Azure Cosmos DB 공급자를 사용할 때 Contains, StartsWith 및 EndsWith 문자열 메서드를 사용하는 쿼리가 변환됩니다.Queries that use the string methods Contains, StartsWith, and EndsWith are now translated when using the Azure Cosmos DB provider.

설명서는 문제 #2079에서 찾아볼 수 있습니다.Documentation is tracked by issue #2079.