Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 88acdca

Browse files
committedFeb 22, 2024
Add new articles
1 parent 7e0c418 commit 88acdca

File tree

6 files changed

+267
-0
lines changed

6 files changed

+267
-0
lines changed
 

‎entityframework-plus.net/pages/ef-core-docs/_sidebar.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
- Misc
55
- [LINQ Dynamic](documentations/linq-dynamic/ef-core-linq-dynamic.md)
66
- [Audit](documentations/audit/ef-core-audit.md)
7+
- [Sync Properties](documentations/sync-properties-from-database-values/ef-core-sync-properties-from-database-values.md)
78
- Query
89
- [Query Cache](documentations/query-cache/ef-core-query-cache.md)
910
- [Query Deferred](documentations/query-deferred/ef-core-query-deferred.md)
1011
- [Query Filter](documentations/query-filter/ef-core-query-filter.md)
1112
- [Query Future](documentations/query-future/ef-core-query-future.md)
13+
- [Query Hint](documentations/query-hint/ef-core-query-hint.md)
1214
- [Query IncludeFilter](documentations/query-include-filter/ef-core-query-include-filter.md)
1315
- [Query IncludeOptimized](documentations/query-include-optimized/ef-core-query-include-optimized.md)
1416
- Enterprise Features
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
Permalink: ef-core-query-hint
3+
Name: Query Hint
4+
LastMod: 2024-02-18
5+
---
6+
7+
# Query Hint
8+
9+
## Description
10+
11+
The Query Hint feature allows you to add hints to your queries with an easy-to-use syntax.
12+
13+
For example, consider the following LINQ query: `context.Products.ToList()`. This query generates the following SQL:
14+
15+
```sql
16+
SELECT [p].[ProductID], [p].[Description], [p].[Name]
17+
FROM [Products] AS [p]
18+
```
19+
20+
If you want to add a hint such as `NOLOCK` to this query, there is currently no easy way through EF Core.
21+
22+
However, the Query Hint feature allows you to do it easily by changing the LINQ to:
23+
24+
```csharp
25+
context.Products.WithHint(SqlServerTableHintFlags.NOLOCK).ToList();
26+
```
27+
28+
This will generate the following SQL:
29+
30+
```sql
31+
SELECT [p].[ProductID], [p].[Description], [p].[Name]
32+
FROM [Products] AS [p] WITH (NOLOCK)
33+
```
34+
35+
## Supported SQL Server TableHint
36+
37+
You can specify hints directly as a string, for example:
38+
39+
```csharp
40+
context.Products.WithHint("NOLOCK").ToList();
41+
```
42+
43+
You can also specify more than one hint:
44+
45+
```csharp
46+
context.Products.WithHint("NOLOCK, INDEX(IX_ProductName)").ToList();
47+
```
48+
49+
Additionally, you can use one of the following `SqlServerTableHintFlags`:
50+
51+
```csharp
52+
// context.Products.WithHint(SqlServerTableHintFlags.NOLOCK).ToList();
53+
54+
namespace Z.EntityFramework.Plus
55+
{
56+
[Flags]
57+
public enum SqlServerTableHintFlags
58+
{
59+
NONE = 0,
60+
FORCESCAN = 1,
61+
FORCESEEK = 2,
62+
HOLDLOCK = 4,
63+
NOLOCK = 8,
64+
NOWAIT = 16,
65+
PAGLOCK = 32,
66+
READCOMMITTED = 64,
67+
READCOMMITTEDLOCK = 256,
68+
READPAST = 512,
69+
READUNCOMMITTED = 1024,
70+
REPEATABLEREAD = 2048,
71+
ROWLOCK = 4096,
72+
SERIALIZABLE = 8192,
73+
SNAPSHOT = 16384,
74+
TABLOCK = 262144,
75+
TABLOCKX = 16777216,
76+
UPDLOCK = 33554432,
77+
XLOCK = 67108864
78+
}
79+
}
80+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
---
2+
Permalink: ef-core-sync-properties-from-database-values
3+
Name: Sync Properties from Database Values
4+
LastMod: 2024-02-18
5+
---
6+
7+
# Sync Properties from Database Values
8+
9+
## Description
10+
11+
The `SyncPropertiesFromDatabaseValues` method allows you to synchronize one or more properties from a list using the database value.
12+
13+
Let's explain with the following example:
14+
15+
```csharp
16+
var products = GetProductToImport();
17+
18+
using (var context = new EntityContext())
19+
{
20+
context.SyncPropertiesFromDatabaseValues(products, options =>
21+
{
22+
options.ColumnPrimaryKeyExpression = x => new { x.Sku };
23+
options.SyncPropertyExpression = x => new { x.ProductID };
24+
});
25+
26+
context.BulkMerge(products);
27+
}
28+
```
29+
30+
In this example, we want to [bulk merge (Upsert)](https://entityframework-extensions.net/bulk-merge) a list of products. However, the method `GetProductToImport` only provides information like the `Sku`, `Name`, and `Description`. The importation currently doesn't know the `ProductID` as it's coming from an external database.
31+
32+
Since the `Sku` is unique to a product, we can use it as our key to retrieve the `ProductID`.
33+
34+
The following line tells the method to use the `Sku` as the key:
35+
36+
```csharp
37+
options.ColumnPrimaryKeyExpression = x => new { x.Sku };
38+
```
39+
40+
And the following line lets the method know which properties it should retrieve from the database:
41+
42+
```csharp
43+
options.SyncPropertyExpression = x => new { x.ProductID };
44+
```
45+
46+
After the method is executed, we can now easily use the `BulkMerge` method, which will use the `ProductID` that we just retrieved to see if the product already exists or not.
47+
48+
## Options
49+
50+
The method currently supports the following options:
51+
52+
| Name | Description |
53+
| :--- | :---------- |
54+
| ColumnPrimaryKeyExpression | This option allows you to choose which key to use through an expression. |
55+
| ColumnPrimaryKeyNames | This option allows you to choose which key to use by passing a list of strings. |
56+
| PrimaryKeyAndFormula | This option allows you to add any `SQL` text to filter even more what will be joined. |
57+
| SyncPropertyExpression | This option allows you to choose which property to synchronize through an expression. |
58+
| SyncPropertyNames | This option allows you to choose which property to synchronize by passing a list of strings. |
59+
| IgnorePropertyExpression | This option allows you to choose which property to NOT synchronize through an expression. All other properties will be synchronized. |
60+
| IgnorePropertyNames | This option allows you to choose which property to NOT synchronize by passing a list of strings. All other properties will be synchronized. |
61+
62+
## Real Life Scenarios
63+
64+
### Concurrency
65+
66+
One common use of this method is when you want to quickly solve [concurrency issues with the Client Wins](https://entityframework-extensions.net/concurrency#client-wins) scenario to [handle concurrency conflicts](https://learn.microsoft.com/en-us/ef/core/saving/concurrency).
67+
68+
If the concurrency token is currently different from the database, you have to use [GetDatabaseValues](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.changetracking.entityentry.getdatabasevalues) for every entity only to retrieve the latest concurrency value.
69+
70+
Using `SyncPropertiesFromDatabaseValues`, you can retrieve all values at once.
71+
72+
### Retrieving ID
73+
74+
As shown in the example in the description, when using methods such as [BulkUpdate](https://entityframework-extensions.net/bulk-update) and [BulkDelete](https://entityframework-extensions.net/bulk-delete) from Entity Framework Extensions, you do not always have the ID.
75+
76+
While it's possible within Entity Framework Extensions to set a [ColumnPrimaryKeyExpression](https://entityframework-extensions.net/column-primary-key-expression), sometimes it's better to directly use the real key for performance.

‎learndapper.com/pages/nav.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,11 @@
5353
<li><a href="/misc/transaction">Transaction</a></li>
5454
</ul>
5555
</li>
56+
<li>
57+
<a href="/troubleshooting">Troubleshooting</a>
58+
<ul class="nav-level-two">
59+
<li><a href="/troubleshooting/the-connection-had-been-disposed">The connection had been disposed</a></li>
60+
</ul>
61+
</li>
5662
</ul>
5763
</nav>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
title: Dapper Troubleshooting
3+
description: Learn common issue within Dapper. What can cause them and how to solve them.
4+
canonical: /troubleshooting
5+
status: Published
6+
lastmod: 2024-02-22
7+
---
8+
9+
# Dapper Troubleshooting
10+
11+
Learn common issues within Dapper. What can cause them and how to solve them:
12+
13+
- [The connection had been disposed](/troubleshooting/the-connection-had-been-disposed)
14+
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
title: [SOLVED] - Fixing 'The connection had been disposed.'
3+
description: Learn how to fix the 'The connection had been disposed.' error in Dapper. Understand how using async method can cause this.
4+
canonical: /the-connection-had-been-disposed
5+
status: Published
6+
lastmod: 2024-02-22
7+
---
8+
9+
# The connection had been disposed.
10+
11+
## Exception Message
12+
13+
You received the following error message: `The connection had been disposed.`
14+
15+
<div class="image-outer"><img src="https://www.learndapper.com/images/dapper/troubleshooting/the-connection-had-been-disposed/the-connection-had-been-disposed.png" loading="lazy" alt="Exception - The connection had been disposed."></div>
16+
17+
Stack Trace:
18+
19+
```txt
20+
This exception was originally thrown at this call stack:
21+
MySql.Data.MySqlClient.MySqlConnection.Throw(System.Exception)
22+
MySql.Data.MySqlClient.MySqlConnection.OpenAsync(bool, System.Threading.CancellationToken)
23+
Dapper.SqlMapper.QueryAsync<T>(System.Data.IDbConnection, System.Type, Dapper.CommandDefinition) in SqlMapper.Async.cs
24+
Z.Dapper.Plus.Lab.Troubleshooting.ConnectionHasBeenDisposed.ConnnectionHasBeenDisposedExample.AnonymousMethod__0() in Request_ConnectionDisposed.cs
25+
Z.Dapper.Plus.Lab.Troubleshooting.ConnectionHasBeenDisposed.ConnnectionHasBeenDisposedExample() in Request_ConnectionDisposed.cs
26+
```
27+
28+
## Cause
29+
30+
Dapper does not dispose of the connection; it might open and close it but will not dispose of it. Therefore, the issue is likely in your code.
31+
32+
With developers using asynchronous methods everywhere today, the number one cause is probably a misuse of the async method.
33+
34+
Here is a bad async usage example that can randomly throw the error:
35+
36+
```csharp
37+
using (var connection = new MySqlConnection(My.Connection))
38+
{
39+
connection.QueryAsync<Customer>("SELECT * FROM Customer")
40+
}
41+
```
42+
43+
In this example, the [QueryAsync](/dapper-query/selecting-multiple-rows#dapper-queryasync) method might sometimes throw the error if it's executed after we leave the `using` block. Remember, if you do not await a task, the task can be executed at any time.
44+
45+
Here is a method that will throw the error 100% of the time:
46+
47+
```csharp
48+
public static async Task ConnnectionHasBeenDisposedExample()
49+
{
50+
Task task;
51+
52+
using (var connection = new MySqlConnection(My.Connection))
53+
{
54+
task = Task.Run(async () =>
55+
{
56+
await Task.Delay(1000);
57+
await connection.QueryAsync<Customer>("SELECT * FROM Customer");
58+
59+
});
60+
}
61+
62+
await task;
63+
}
64+
```
65+
66+
The method has been tested with [MySql.Data v8.3](https://www.nuget.org/packages/MySql.Data/8.3.0)
67+
68+
As you can see in this example, we run the code in a task to be able to add a delay of 1 second. With this delay, we ensure that we leave the `using` block before the [QueryAsync](/dapper-query/selecting-multiple-rows#dapper-queryasync) method is executed.
69+
70+
## Solution
71+
72+
To solve the `The connection had been disposed.` issue when an async method is wrongly used, you simply have to ensure you are using the `async` method properly by using the `await` keyword.
73+
74+
```csharp
75+
using (var connection = new MySqlConnection(My.Connection))
76+
{
77+
await connection.QueryAsync<Customer>("SELECT * FROM Customer") // .ConfigureAwait(false) if needed
78+
}
79+
```
80+
81+
You can also use the `.ConfigureAwait(false)` method if needed to prevent the continuation from capturing the current synchronization context. This can be beneficial in scenarios where you want to avoid deadlock situations by allowing the continuation to run on a different context.
82+
83+
## Note
84+
85+
It's important to note that different providers or different versions within a provider will throw different errors. However, the solution will always be the same if the cause was a wrong usage of the `Async` method.
86+
87+
## Related Articles
88+
89+
- [Query Async](/dapper-query/selecting-multiple-rows#dapper-queryasync)

0 commit comments

Comments
 (0)
Please sign in to comment.