Skip to content

Commit 8b13c2a

Browse files
committed
Add tests for feature from dotnet#36556
1 parent 3903d9e commit 8b13c2a

14 files changed

+5073
-1
lines changed

src/EFCore/Extensions/EntityFrameworkQueryableExtensions.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2892,6 +2892,31 @@ public static IQueryable<T> Refresh<T>(
28922892
this IQueryable<T> source,
28932893
[NotParameterized] MergeOption mergeOption)
28942894
{
2895+
bool isNotTracked = false;
2896+
2897+
string[] expressionNames = source.Expression.ToString().Split('.');
2898+
if (
2899+
expressionNames.Any(c => c.Contains(nameof(EntityFrameworkQueryableExtensions.AsNoTracking)))
2900+
|| expressionNames.Any(c => c.Contains(nameof(EntityFrameworkQueryableExtensions.AsNoTrackingWithIdentityResolution)))
2901+
|| expressionNames.Any(c => c.Contains(nameof(EntityFrameworkQueryableExtensions.IgnoreAutoIncludes)))
2902+
)
2903+
{
2904+
isNotTracked = true;
2905+
}
2906+
2907+
if (isNotTracked)
2908+
{
2909+
throw new InvalidOperationException(CoreStrings.RefreshNonTrackingQuery);
2910+
}
2911+
2912+
MergeOption[] otherMergeOptions = Enum.GetValues<MergeOption>().Where(v => v != mergeOption).ToArray();
2913+
2914+
bool anyOtherMergeOption = expressionNames.Any(c => otherMergeOptions.Any(o => c.Contains(o.ToString())));
2915+
if (anyOtherMergeOption)
2916+
{
2917+
throw new InvalidOperationException(CoreStrings.RefreshMultipleMergeOptions);
2918+
}
2919+
28952920
return
28962921
source.Provider is EntityQueryProvider
28972922
? source.Provider.CreateQuery<T>(

src/EFCore/Properties/CoreStrings.Designer.cs

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/EFCore/Properties/CoreStrings.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,12 @@
15711571
<data name="ReferenceMustBeLoaded" xml:space="preserve">
15721572
<value>The navigation '{1_entityType}.{0_navigation}' cannot have 'IsLoaded' set to false because the referenced entity is non-null and is therefore loaded.</value>
15731573
</data>
1574+
<data name="RefreshMultipleMergeOptions" xml:space="preserve">
1575+
<value>Unable to refresh query with multiple merge options!</value>
1576+
</data>
1577+
<data name="RefreshNonTrackingQuery" xml:space="preserve">
1578+
<value>Unable to refresh non-tracking query!</value>
1579+
</data>
15741580
<data name="RelationshipCannotBeInverted" xml:space="preserve">
15751581
<value>The principal and dependent ends of the relationship cannot be changed once foreign key or principal key properties have been specified. Remove the conflicting configuration.</value>
15761582
</data>
Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
# Entity Framework Core - MergeOptionFeature Test Guide
2+
3+
This guide provides a comprehensive overview of all tests in the `Microsoft.EntityFrameworkCore.MergeOptionFeature` namespace. These tests are designed to validate the refresh functionality (merge options) across various Entity Framework Core features when entities are refreshed from the database after external changes.
4+
5+
## Background
6+
7+
The MergeOptionFeature tests simulate scenarios where database data changes externally (outside of the current EF Core context), and then test how EF Core handles refreshing entities to reflect these external changes. This is crucial for applications that need to stay synchronized with database changes made by other processes, users, or systems.
8+
9+
---
10+
11+
## Test 1: RefreshFromDb_Northwind_SqlServer_Test
12+
13+
### 1.1 EF Core Features Tested
14+
This test focuses on **basic entity refresh functionality** using the standard Northwind database model. It tests core scenarios like:
15+
- Entity states (Unchanged, Modified, Added, Deleted)
16+
- Query terminating operators (ToList, FirstOrDefault, etc.)
17+
- Include operations for loading related data
18+
- Lazy loading with proxies
19+
- Non-tracking queries
20+
- Streaming vs buffering query consumption
21+
22+
### 1.2 Test Context and Model
23+
- **Context**: Uses `NorthwindQuerySqlServerFixture<NoopModelCustomizer>`
24+
- **Model**: Standard Northwind database (Customers, Orders, Products, etc.)
25+
- **Elements**: Real-world entities with established relationships
26+
- **Database**: SQL Server with pre-seeded Northwind data
27+
28+
### 1.3 Global Test Purpose
29+
Validates that EF Core's basic refresh mechanisms work correctly across different entity states and query patterns. Ensures that external database changes are properly reflected when entities are refreshed, and that the change tracking system maintains consistency.
30+
31+
### 1.4 Test Methods Overview
32+
- **Entity State Tests**: Verify refresh behavior for entities in different states (Added, Modified, Deleted, Unchanged)
33+
- **Query Pattern Tests**: Test refresh with different LINQ terminating operators and consumption patterns
34+
- **Include Tests**: Validate refresh behavior when related entities are loaded via Include operations
35+
- **Proxy Tests**: Test lazy loading proxy behavior during refresh operations
36+
- **Error Condition Tests**: Ensure proper exceptions are thrown for invalid refresh scenarios (e.g., non-tracking queries)
37+
38+
---
39+
40+
## Test 2: RefreshFromDb_ComplexTypes_SqlServer_Test
41+
42+
### 2.1 EF Core Features Tested
43+
This test focuses on **Complex Types** - value objects that are embedded within entities but don't have their own identity. Features tested:
44+
- Owned types (both collection and non-collection)
45+
- Complex properties of value and reference types
46+
- Refresh behavior for nested value objects
47+
- JSON serialization of complex data
48+
49+
### 2.2 Test Context and Model
50+
- **Context**: Custom `ComplexTypesContext` with specially designed entities
51+
- **Model**:
52+
- `Product` with owned `ProductDetails` and owned collection `Reviews`
53+
- `Customer` with complex property `ContactInfo` and owned collection `Addresses`
54+
- **Elements**: Demonstrates embedding complex objects within main entities
55+
- **Database**: Custom tables with JSON columns and nested object storage
56+
57+
### 2.3 Global Test Purpose
58+
Validates that EF Core correctly refreshes complex types and owned entities when the underlying database data changes. Ensures that nested objects maintain their structure and relationships during refresh operations.
59+
60+
### 2.4 Test Methods Overview
61+
- **Collection Owned Types**: Tests refreshing when owned collections (like Reviews) change externally
62+
- **Non-Collection Owned Types**: Tests refreshing simple owned objects (like ProductDetails)
63+
- **Collection Complex Properties**: Tests refreshing complex collections (like Addresses)
64+
- **Non-Collection Complex Properties**: Tests refreshing simple complex objects (like ContactInfo)
65+
66+
---
67+
68+
## Test 3: RefreshFromDb_ComputedColumns_SqlServer_Test
69+
70+
### 3.1 EF Core Features Tested
71+
This test focuses on **Computed Columns** - database columns whose values are calculated by the database engine rather than stored directly. Features tested:
72+
- Properties mapped to computed columns
73+
- Mathematical computations (Price * Quantity)
74+
- String concatenation formulas
75+
- Date formatting expressions
76+
- Database-generated calculated values
77+
78+
### 3.2 Test Context and Model
79+
- **Context**: Custom `ComputedColumnsContext` with computed column definitions
80+
- **Model**:
81+
- `Product` with `TotalValue` (Price * Quantity) and `Description` (Name + Price formatted)
82+
- `Order` with `FormattedOrderDate` (formatted date string)
83+
- **Elements**: SQL Server computed column expressions using T-SQL functions
84+
- **Database**: Tables with computed columns using SQL Server-specific syntax
85+
86+
### 3.3 Global Test Purpose
87+
Ensures that computed columns are correctly refreshed when their underlying base columns change. Validates that database-calculated values are properly materialized into .NET properties during refresh operations.
88+
89+
### 3.4 Test Methods Overview
90+
- **Basic Computed Columns**: Tests refreshing entities where computed values depend on other columns
91+
- **Query Integration**: Validates that computed columns work correctly in LINQ queries
92+
- **Database-Generated Updates**: Tests refresh when computed columns use database functions like date formatting
93+
94+
---
95+
96+
## Test 4: RefreshFromDb_GlobalFilters_SqlServer_Test
97+
98+
### 4.1 EF Core Features Tested
99+
This test focuses on **Global Query Filters** - automatically applied WHERE clauses that filter entities globally across all queries. Features tested:
100+
- Multi-tenancy filtering (filtering by TenantId)
101+
- Soft delete patterns (filtering out deleted records)
102+
- Dynamic filter contexts (changing filter values at runtime)
103+
- Filter bypass using `IgnoreQueryFilters()`
104+
105+
### 4.2 Test Context and Model
106+
- **Context**: Custom `GlobalFiltersContext` with tenant-aware filtering
107+
- **Model**:
108+
- `Product` with multi-tenancy filter (`TenantId == CurrentTenantId`)
109+
- `Order` with both multi-tenancy and soft delete filters (`TenantId == CurrentTenantId && !IsDeleted`)
110+
- **Elements**: Context property `TenantId` that controls filtering behavior
111+
- **Database**: Tables with tenant and soft delete columns
112+
113+
### 4.3 Global Test Purpose
114+
Validates that global query filters work correctly during refresh operations, ensuring entities remain properly filtered even when refreshed from the database. Tests that filter context changes affect entity visibility as expected.
115+
116+
### 4.4 Test Methods Overview
117+
- **Basic Filter Tests**: Ensures filtered entities refresh correctly within their filter scope
118+
- **Filter Bypass Tests**: Tests `IgnoreQueryFilters()` to access normally filtered entities
119+
- **Dynamic Context Tests**: Validates changing filter context (tenant switching) affects entity visibility
120+
- **Soft Delete Tests**: Tests the common soft delete pattern with global filters
121+
122+
---
123+
124+
## Test 5: RefreshFromDb_ManyToMany_SqlServer_Test
125+
126+
### 5.1 EF Core Features Tested
127+
This test focuses on **Many-to-Many Relationships** without explicit join entities. Features tested:
128+
- Modern EF Core many-to-many configuration
129+
- Automatic join table management
130+
- Bidirectional relationship updates
131+
- Collection navigation refresh
132+
- Multiple relationship manipulation
133+
134+
### 5.2 Test Context and Model
135+
- **Context**: Custom `ManyToManyContext` with modern many-to-many setup
136+
- **Model**:
137+
- `Student` ? `Course` (StudentCourse join table)
138+
- `Author` ? `Book` (AuthorBook join table)
139+
- **Elements**: Uses `HasMany().WithMany().UsingEntity()` configuration
140+
- **Database**: Automatic join tables managed by EF Core
141+
142+
### 5.3 Global Test Purpose
143+
Ensures that many-to-many relationships are correctly refreshed when join table records change externally. Validates that both sides of the relationship reflect changes when navigation collections are refreshed.
144+
145+
### 5.4 Test Methods Overview
146+
- **Add Relationships**: Tests adding new many-to-many connections externally and refreshing
147+
- **Remove Relationships**: Tests removing connections and refreshing to reflect removal
148+
- **Bidirectional Updates**: Ensures both sides of relationships update when refreshed
149+
- **Multiple Operations**: Tests handling multiple relationship changes simultaneously
150+
151+
---
152+
153+
## Test 6: RefreshFromDb_PrimitiveCollections_SqlServer_Test
154+
155+
### 6.1 EF Core Features Tested
156+
This test focuses on **Primitive Collections** - collections of primitive types (string, int, Guid) stored as JSON in database columns. Features tested:
157+
- JSON column mapping for collections
158+
- Primitive collection configuration
159+
- Collection serialization/deserialization
160+
- Empty collection handling
161+
- Various primitive types (strings, integers, GUIDs)
162+
163+
### 6.2 Test Context and Model
164+
- **Context**: Custom `PrimitiveCollectionsContext` with JSON column storage
165+
- **Model**:
166+
- `Product` with `List<string> Tags` stored as JSON
167+
- `Blog` with `List<int> Ratings` stored as JSON
168+
- `User` with `List<Guid> RelatedIds` stored as JSON
169+
- **Elements**: Uses `PrimitiveCollection()` configuration method
170+
- **Database**: JSON columns in SQL Server for collection storage
171+
172+
### 6.3 Global Test Purpose
173+
Validates that primitive collections stored as JSON are correctly refreshed when the underlying JSON data changes. Ensures proper serialization/deserialization during refresh operations.
174+
175+
### 6.4 Test Methods Overview
176+
- **String Collections**: Tests refreshing collections of strings (tags, categories)
177+
- **Number Collections**: Tests refreshing collections of integers (ratings, scores)
178+
- **GUID Collections**: Tests refreshing collections of GUIDs (identifiers)
179+
- **Empty Collections**: Tests edge cases with empty collections and null handling
180+
181+
---
182+
183+
## Test 7: RefreshFromDb_ShadowProperties_SqlServer_Test
184+
185+
### 7.1 EF Core Features Tested
186+
This test focuses on **Shadow Properties** - properties that exist in the EF model and database but not as .NET class properties. Features tested:
187+
- Shadow property configuration
188+
- Accessing shadow properties via `Entry().Property()`
189+
- Shadow foreign keys for relationships
190+
- Querying using `EF.Property<T>()` method
191+
- Mixed shadow and regular properties
192+
193+
### 7.2 Test Context and Model
194+
- **Context**: Custom `ShadowPropertiesContext` with shadow property definitions
195+
- **Model**:
196+
- `Product` with shadow properties `CreatedBy`, `CreatedAt`, `LastModified`
197+
- `Order` with shadow foreign key `CustomerId`
198+
- **Elements**: Properties defined in model but not in .NET classes
199+
- **Database**: Regular database columns mapped to shadow properties
200+
201+
### 7.3 Global Test Purpose
202+
Ensures that shadow properties are correctly refreshed even though they don't exist as .NET properties. Validates that the entity framework properly manages these "invisible" properties during refresh operations.
203+
204+
### 7.4 Test Methods Overview
205+
- **Basic Shadow Properties**: Tests refreshing shadow properties like audit fields
206+
- **Mixed Property Types**: Tests refreshing entities with both regular and shadow properties
207+
- **Shadow Foreign Keys**: Tests refreshing shadow properties used as foreign keys
208+
- **Query Integration**: Tests using shadow properties in LINQ queries with `EF.Property<T>()`
209+
210+
---
211+
212+
## Test 8: RefreshFromDb_TableSharing_SqlServer_Test
213+
214+
### 8.1 EF Core Features Tested
215+
This test focuses on **Table Sharing** - multiple entity types mapping to the same database table. Features tested:
216+
- Multiple entities sharing table storage
217+
- Shared non-key columns between entities
218+
- Table Per Type (TPT) inheritance patterns
219+
- One-to-one relationships with shared storage
220+
- Independent entity updates on shared tables
221+
222+
### 8.2 Test Context and Model
223+
- **Context**: Custom `TableSharingContext` with multiple entities per table
224+
- **Model**:
225+
- `Person` and `Employee` sharing "People" table
226+
- `Blog` and `BlogMetadata` sharing "Blogs" table
227+
- `Vehicle` and `Car` (inheritance) sharing "Vehicles" table
228+
- **Elements**: Uses `ToTable()` to map multiple entities to same table
229+
- **Database**: Single tables storing data for multiple entity types
230+
231+
### 8.3 Global Test Purpose
232+
Validates that entities sharing table storage are correctly refreshed when shared columns change. Ensures that updates to shared data are visible to all entity types that map to the same table.
233+
234+
### 8.4 Test Methods Overview
235+
- **Shared Column Updates**: Tests when shared columns (like Name) are updated externally
236+
- **Independent Entity Updates**: Tests entity-specific columns on shared tables
237+
- **Inheritance Scenarios**: Tests table sharing with inheritance hierarchies
238+
- **Relationship Sharing**: Tests entities in relationships that share storage
239+
240+
---
241+
242+
## Test 9: RefreshFromDb_ValueConverters_SqlServer_Test
243+
244+
### 9.1 EF Core Features Tested
245+
This test focuses on **Value Converters** - custom conversion logic between .NET types and database storage types. Features tested:
246+
- Enum to string conversion
247+
- Collection to JSON conversion
248+
- DateTime to string formatting
249+
- GUID to string representation
250+
- Custom value object conversion
251+
- Built-in and custom converter patterns
252+
253+
### 9.2 Test Context and Model
254+
- **Context**: Custom `ValueConvertersContext` with various converter types
255+
- **Model**:
256+
- `Product` with enum-to-string and collection-to-JSON converters
257+
- `User` with DateTime-to-string and GUID-to-string converters
258+
- `Order` with custom `Money` value object converter
259+
- **Elements**: Uses `HasConversion()` method with custom conversion logic
260+
- **Database**: Storage types different from .NET types (strings, JSON, decimals)
261+
262+
### 9.3 Global Test Purpose
263+
Ensures that value converters work correctly during refresh operations, properly converting between database storage formats and .NET types. Validates that external changes in the database format are correctly converted back to .NET objects.
264+
265+
### 9.4 Test Methods Overview
266+
- **Enum Converters**: Tests enum values stored as strings in database
267+
- **JSON Converters**: Tests complex objects serialized as JSON
268+
- **DateTime Converters**: Tests custom date formatting patterns
269+
- **GUID Converters**: Tests GUID-to-string conversion patterns
270+
- **Value Object Converters**: Tests custom value objects with conversion logic
271+
272+
---
273+
274+
## Key Concepts for Understanding These Tests
275+
276+
### Entity Refresh (ReloadAsync)
277+
The core operation being tested - refreshing an entity from the database to pick up external changes.
278+
279+
### External Changes Simulation
280+
Tests use `ExecuteSqlRawAsync()` to simulate changes made outside the current EF context, representing real-world scenarios like other applications, users, or processes modifying data.
281+
282+
### Change Tracking Integration
283+
All tests validate that refresh operations work correctly with EF's change tracking system, maintaining consistency between tracked entities and database state.
284+
285+
### Provider-Specific Testing
286+
These are SQL Server-specific tests, ensuring that refresh functionality works with SQL Server's specific features and data types.
287+
288+
### Comprehensive Coverage
289+
Together, these tests cover the full spectrum of EF Core features to ensure refresh functionality works across all scenarios that applications might encounter.
290+
291+
---
292+
293+
## Running the Tests
294+
295+
To run these tests:
296+
1. Ensure SQL Server is available (LocalDB is sufficient)
297+
2. Run `restore.cmd` to restore dependencies
298+
3. Run `. .\activate.ps1` to setup the development environment
299+
4. Execute tests using `dotnet test` or Visual Studio Test Explorer
300+
5. Tests will create temporary databases and clean up automatically
301+
302+
Each test is designed to be independent and can be run individually or as a suite to validate the entire refresh functionality across all EF Core features.

0 commit comments

Comments
 (0)