|
1 | 1 | ---
|
2 | 2 | title: Client vs. Server Evaluation - EF Core
|
3 |
| -author: rowanmiller |
4 |
| -ms.date: 10/27/2016 |
| 3 | +author: smitpatel |
| 4 | +ms.date: 10/01/2019 |
5 | 5 | ms.assetid: 8b6697cc-7067-4dc2-8007-85d80503d123
|
6 | 6 | uid: core/querying/client-eval
|
7 | 7 | ---
|
8 | 8 | # Client vs. Server Evaluation
|
9 | 9 |
|
10 |
| -Entity Framework Core supports parts of the query being evaluated on the client and parts of it being pushed to the database. It is up to the database provider to determine which parts of the query will be evaluated in the database. |
| 10 | +In order to evaluate query on server as much as possible, Entity Framework Core converts parts of the query which can be evaluated on client side to convert them to parameters and rest of the query with generated parameters is given to database providers to determine what should be equivalent database query to do evaluation on the server. EF Core supports partial client evaluation in top level projection (essentially, the last call to `Select()`). If parts of query in top level projection cannot be translated to server, EF Core will fetch required data from server and evaluate it on client. If EF Core detects expression which cannot be translated to server in any other place then it throws runtime exception. See [how query works](xref:core/querying/how-query-works) to understand how EF Core determines what cannot be translated to server. |
11 | 11 |
|
12 |
| -> [!TIP] |
13 |
| -> You can view this article's [sample](https://github.com/aspnet/EntityFramework.Docs/tree/master/samples/core/Querying) on GitHub. |
| 12 | +> [!NOTE] |
| 13 | +> Prior to version 3.0, Entity Framework Core supported parts of query to be evaluated on client anywhere in the query. See the [previous versions section](#previous-versions) for more details. |
14 | 14 |
|
15 | 15 | ## Client evaluation
|
16 | 16 |
|
17 |
| -In the following example a helper method is used to standardize URLs for blogs that are returned from a SQL Server database. Because the SQL Server provider has no insight into how this method is implemented, it is not possible to translate it into SQL. All other aspects of the query are evaluated in the database, but passing the returned `URL` through this method is performed on the client. |
18 |
| - |
19 |
| -<!-- [!code-csharp[Main](samples/core/Querying/ClientEval/Sample.cs?highlight=6)] --> |
20 |
| -``` csharp |
21 |
| -var blogs = context.Blogs |
22 |
| - .OrderByDescending(blog => blog.Rating) |
23 |
| - .Select(blog => new |
24 |
| - { |
25 |
| - Id = blog.BlogId, |
26 |
| - Url = StandardizeUrl(blog.Url) |
27 |
| - }) |
28 |
| - .ToList(); |
29 |
| -``` |
| 17 | +> [!TIP] |
| 18 | +> You can view this article's [sample](https://github.com/aspnet/EntityFramework.Docs/tree/master/samples/core/Querying) on GitHub. |
30 | 19 |
|
31 |
| -<!-- [!code-csharp[Main](samples/core/Querying/ClientEval/Sample.cs)] --> |
32 |
| -``` csharp |
33 |
| -public static string StandardizeUrl(string url) |
34 |
| -{ |
35 |
| - url = url.ToLower(); |
| 20 | +In the following example a helper method is used to standardize URLs for blogs which are returned from a SQL Server database. Because the SQL Server provider has no insight into how this method is implemented, it is not possible to translate it into SQL. All other aspects of the query are evaluated in the database, but passing the returned `URL` through this method is performed on the client. |
36 | 21 |
|
37 |
| - if (!url.StartsWith("http://")) |
38 |
| - { |
39 |
| - url = string.Concat("http://", url); |
40 |
| - } |
| 22 | +[!code-csharp[Main](../../../samples/core/Querying/ClientEval/Sample.cs#ClientProjection)] |
41 | 23 |
|
42 |
| - return url; |
43 |
| -} |
44 |
| -``` |
| 24 | +[!code-csharp[Main](../../../samples/core/Querying/ClientEval/Sample.cs#ClientMethod)] |
45 | 25 |
|
46 |
| -## Client evaluation performance issues |
| 26 | +## Unsupported client evaluation |
47 | 27 |
|
48 |
| -While client evaluation can be very useful, in some instances it can result in poor performance. Consider the following query, where the helper method is now used in a filter. Because this can't be performed in the database, all the data is pulled into memory and then the filter is applied on the client. Depending on the amount of data, and how much of that data is filtered out, this could result in poor performance. |
| 28 | +While client evaluation can be very useful, in some instances it can result in poor performance. Consider the following query, in which the helper method is now used in a where filter. Because this can't be performed in the database, all the data needs to be pulled into memory and then the filter needs to be applied on the client. Depending on the amount of data on the server, and how much of that data is filtered out, this could result in poor performance. Hence Entity Framework Core blocks such client evaluation and throws a runtime exception. |
49 | 29 |
|
50 |
| -<!-- [!code-csharp[Main](samples/core/Querying/ClientEval/Sample.cs)] --> |
51 |
| -``` csharp |
52 |
| -var blogs = context.Blogs |
53 |
| - .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet")) |
54 |
| - .ToList(); |
55 |
| -``` |
| 30 | +[!code-csharp[Main](../../../samples/core/Querying/ClientEval/Sample.cs#ClientWhere)] |
| 31 | + |
| 32 | +## Explicit client evaluation |
| 33 | + |
| 34 | +In certain instances user may need to force into client evaluation explicitly like following cases |
| 35 | + |
| 36 | +- Data is small so that doing evaluation on client does not cause huge performance penalty. |
| 37 | +- LINQ operator being used has no server side translation. |
| 38 | + |
| 39 | +In such cases, user can explicitly opt into client evaluation by calling methods like `AsEnumerable` (or `AsAsyncEnumerable` for async). |
| 40 | + |
| 41 | +[!code-csharp[Main](../../../samples/core/Querying/ClientEval/Sample.cs#ExplicitClientEval)] |
| 42 | + |
| 43 | +## Potential memory leak in client evaluation |
| 44 | + |
| 45 | +Since query translation and compilation is expensive, by default, EF Core caches compiled query plan. With client evaluation allowed in the top projection, the cached delegate can use client code. In most cases, EF Core generates parameters for client parts of the tree so that their values can be replaced when reusing the query plan. But with certain constants in expression tree, it cannot be converted to parameters. If the client code contains such constants then those objects cannot be garbage collected. If such object contains DbContext or other services inside it then it could cause memory usage of the app to grow over time. In order to avoid potential memory leak, EF Core throws exception whenever it encounter constants of type which cannot be mapped in current database providers. Common causes and their solutions are as follows: |
| 46 | + |
| 47 | +- **Using instance method**: When using instance methods in client projection, the expression tree contains constant of the instance. If your method does not use any data from instance, consider making the method static. If you need instance data in the method body, then pass specific data as an argument to the method. |
| 48 | +- **Passing constant argument to method**: This case arises generally by using `this` in argument to client method. Consider splitting argument in to multiple arguments which can be mapped by database provider based on components being used in the method body. |
| 49 | +- **Other constants**: If constant is encountered in any other case then you can evaluate if constant is really needed in processing. If it is necessary to have that constant or if you cannot use solution to above cases, then create a local variable to store the value and use local variable in the query. EF Core will convert it to parameter. |
56 | 50 |
|
57 |
| -## Client evaluation logging |
| 51 | +## Previous versions |
58 | 52 |
|
59 |
| -By default, EF Core will log a warning when client evaluation is performed. See [Logging](../miscellaneous/logging.md) for more information on viewing logging output. |
| 53 | +The following section is applicable to EF Core versions prior to 3.0. |
60 | 54 |
|
61 |
| -## Optional behavior: throw an exception for client evaluation |
| 55 | +EF Core supported client evaluation in any part query hence queries similar to one posted in [Unsupported client evaluation](#unsupported-client-evaluation) section worked correctly but it caused performance issue without users noticing. In order to notify users that query was being evaluated on client, EF Core logged client evaluation warning. See [Logging](xref:core/miscellaneous/logging) for more information on viewing logging output. |
62 | 56 |
|
63 |
| -You can change the behavior when client evaluation occurs to either throw or do nothing. This is done when setting up the options for your context - typically in `DbContext.OnConfiguring`, or in `Startup.cs` if you are using ASP.NET Core. |
| 57 | +Optionally EF Core allowed to change default behavior to throw exception or do nothing when performing client evaluation except for projection (exception throwing would make it similar to the behavior in 3.0). To change the behavior you need to configure warnings while setting up the options for your context - typically in `DbContext.OnConfiguring`, or in `Startup.cs` if you are using ASP.NET Core. |
64 | 58 |
|
65 |
| -<!-- [!code-csharp[Main](samples/core/Querying/ClientEval/ThrowOnClientEval/BloggingContext.cs?highlight=5)] --> |
66 |
| -``` csharp |
| 59 | +```csharp |
67 | 60 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
68 | 61 | {
|
69 | 62 | optionsBuilder
|
|
0 commit comments