Skip to content

Commit

Permalink
Refactor and improve async documentation
Browse files Browse the repository at this point in the history
* Merge query and saving async pages
* Move under fundamentals
* Document ambiguous invocation issues with System.Interactive.Async
  and show workaround.

Closes #1722
Related to #1332
Related to dotnet/efcore#18124
  • Loading branch information
roji committed Sep 2, 2020
1 parent 98e132b commit 1f78f16
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 135 deletions.
10 changes: 10 additions & 0 deletions .openpublishing.redirection.json
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,16 @@
"redirect_url": "/ef/core/modeling/inheritance",
"redirect_document_id": false
},
{
"source_path": "entity-framework/core/querying/async.md",
"redirect_url": "/ef/core/miscellaneous/async.md/",
"redirect_document_id": false
},
{
"source_path": "entity-framework/core/saving/async.md",
"redirect_url": "/ef/core/miscellaneous/async.md/",
"redirect_document_id": false
},
{
"source_path": "entity-framework/core/modeling/relational/index.md",
"redirect_url": "/ef/core/modeling/",
Expand Down
46 changes: 46 additions & 0 deletions entity-framework/core/miscellaneous/async.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: Asynchronous Programming - EF Core
description: Querying and saving data asynchronously with Entity Framework Core
author: roji
ms.date: 9/2/2020
ms.assetid: 38f71624-7a68-4c72-a918-3e7b858ef090
uid: core/miscellaneous/async
---
# Asynchronous Programming

Asynchronous queries avoid blocking a thread while the query is executed in the database. Async queries are important for keeping a responsive UI in thick-client applications, and can also increase throughput in web applications where they free up the thread to service other requests in web applications.

Following the .NET standard, EF Core provides asynchronous counterparts to all synchronous methods which perform I/O. These have the same effects as the sync methods, and can be used with the C# `async` and `await` keywords. For example, instead of using DbContext.SaveChanges, which will block a thread while database I/O is performed, DbContext.SaveChangesAsync can be used:

[!code-csharp[Main](../../../samples/core/Miscellaneous/Async/Program.cs#SaveChangesAsync)]

For more information,, see [the general C# asynchronous programming docs](/dotnet/csharp/async).

> [!WARNING]
> EF Core doesn't support multiple parallel operations being run on the same context instance. You should always wait for an operation to complete before beginning the next operation. This is typically done by using the `await` keyword on each async operation.
# Async LINQ operators

In order to support executing LINQ queries asynchronously, EF Core provides a set of async extension methods which execute the query and return results. These counterparts to the standard, synchronous LINQ operators include ToListAsync, SingleAsync, AsAsyncEnumerable, etc.:

[!code-csharp[Main](../../../samples/core/Miscellaneous/Async/Program.cs#ToListAsync)]

Note that there are no async versions of some LINQ operators such as Where or OrderBy, because these only build up the LINQ expression tree and don't cause the query to be executed in the database. Only operators which cause query execution have async counterparts.

> [!IMPORTANT]
> The EF Core async extension methods are defined in the `Microsoft.EntityFrameworkCore` namespace. This namespace must be imported for the methods to be available.
# Client-side async LINQ operators

The async LINQ operators discussed above can only be used on EF queries - you cannot use them with client-side LINQ to Objects query. To perform client-side async LINQ operations outside of EF, use the [System.Interactive.Async package](https://www.nuget.org/packages/System.Interactive.Async); this can be especially useful for performing operations on the client that cannot be translated for evaluation at the server.

Unfortunately, referencing System.Interactive.Async causes ambiguous invocation compilation errors on LINQ operators applied to EF's DbSets; this makes it hard to use both EF and System.Interactive.Async in the same project. To work around this issue, add AsQueryable to your DbSet:

```c#
var groupedHighlyRatedBlogs = await context.Blogs
.AsQueryable()
.Where(b => b.Rating > 3) // server-evaluated
.AsAsyncEnumerable()
.GroupBy(b => b.Rating) // client-evaluated
.ToListAsync();
```
21 changes: 0 additions & 21 deletions entity-framework/core/querying/async.md

This file was deleted.

18 changes: 0 additions & 18 deletions entity-framework/core/saving/async.md

This file was deleted.

6 changes: 2 additions & 4 deletions entity-framework/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
items:
- name: Connection strings
href: core/miscellaneous/connection-strings.md
- name: Asynchronous Programming
href: core/miscellaneous/async.md
- name: Logging
href: core/miscellaneous/logging.md
- name: Connection resiliency
Expand Down Expand Up @@ -181,8 +183,6 @@
href: core/querying/complex-query-operators.md
- name: Load related data
href: core/querying/related-data.md
- name: Asynchronous queries
href: core/querying/async.md
- name: Raw SQL queries
href: core/querying/raw-sql.md
- name: Global query filters
Expand All @@ -205,8 +205,6 @@
href: core/saving/concurrency.md
- name: Transactions
href: core/saving/transactions.md
- name: Asynchronous saving
href: core/saving/async.md
- name: Disconnected entities
href: core/saving/disconnected-entities.md
- name: Explicit values for generated properties
Expand Down
14 changes: 14 additions & 0 deletions samples/core/Miscellaneous/Async/Async.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>EFAsync</RootNamespace>
<AssemblyName>EFAsync</AssemblyName>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.7" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.7" />
</ItemGroup>

</Project>
45 changes: 45 additions & 0 deletions samples/core/Miscellaneous/Async/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

namespace EFAsync
{
public class Program
{
static async Task Main(string[] args)
{
await using var context = new BloggingContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

#region SaveChangesAsync
var blog = new Blog { Url = "http://sample.com" };
context.Blogs.Add(blog);
await context.SaveChangesAsync();
#endregion

#region ToListAsync
var blogs = await context.Blogs.Where(b => b.Rating > 3).ToListAsync();
#endregion
}
}

public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFAsync;Trusted_Connection=True;ConnectRetryCount=0");
}
}

public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
}
}
20 changes: 0 additions & 20 deletions samples/core/Querying/Async/Sample.cs

This file was deleted.

19 changes: 19 additions & 0 deletions samples/core/Samples.sln
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QueryFiltersNavigations", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Collations", "Miscellaneous\Collations\Collations.csproj", "{62C86664-49F4-4C59-A2EC-1D70D85149D9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Async", "Miscellaneous\Async\Async.csproj", "{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -467,6 +469,22 @@ Global
{62C86664-49F4-4C59-A2EC-1D70D85149D9}.Release|x64.Build.0 = Release|Any CPU
{62C86664-49F4-4C59-A2EC-1D70D85149D9}.Release|x86.ActiveCfg = Release|Any CPU
{62C86664-49F4-4C59-A2EC-1D70D85149D9}.Release|x86.Build.0 = Release|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Debug|ARM.ActiveCfg = Debug|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Debug|ARM.Build.0 = Debug|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Debug|x64.ActiveCfg = Debug|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Debug|x64.Build.0 = Debug|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Debug|x86.ActiveCfg = Debug|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Debug|x86.Build.0 = Debug|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Release|Any CPU.Build.0 = Release|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Release|ARM.ActiveCfg = Release|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Release|ARM.Build.0 = Release|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Release|x64.ActiveCfg = Release|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Release|x64.Build.0 = Release|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Release|x86.ActiveCfg = Release|Any CPU
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -490,6 +508,7 @@ Global
{E8AD02D7-8AFB-4233-BDAF-F0AEF986F1F3} = {8695C7BE-F9B2-477A-AD7B-C15DC5418F66}
{34C237C8-DD12-4C14-9B15-B7F85C218CDB} = {8695C7BE-F9B2-477A-AD7B-C15DC5418F66}
{62C86664-49F4-4C59-A2EC-1D70D85149D9} = {85AFD7F1-6943-40FE-B8EC-AA9DBB42CCA6}
{1DA2B6AD-F71A-4224-92EB-3D0EE6E68BF4} = {85AFD7F1-6943-40FE-B8EC-AA9DBB42CCA6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {20C98D35-54EF-46A6-8F3B-1855C1AE4F70}
Expand Down
12 changes: 0 additions & 12 deletions samples/core/Saving/Async/Blog.cs

This file was deleted.

15 changes: 0 additions & 15 deletions samples/core/Saving/Async/BloggingContext.cs

This file was deleted.

12 changes: 0 additions & 12 deletions samples/core/Saving/Async/Post.cs

This file was deleted.

33 changes: 0 additions & 33 deletions samples/core/Saving/Async/Sample.cs

This file was deleted.

0 comments on commit 1f78f16

Please sign in to comment.