Skip to content

Commit 0de9e89

Browse files
CopilotBillWagnerT-Gro
authored
Replace deprecated type provider with Entity Framework Core in F# query expressions documentation (#48751)
* Initial plan * Replace deprecated type provider with modern in-memory query examples Co-authored-by: BillWagner <[email protected]> * Final fixes: handle optional Age field properly and add AI usage disclosure Co-authored-by: BillWagner <[email protected]> * Remove redundant query-expressions.fsproj and integrate files into parent project Co-authored-by: BillWagner <[email protected]> * Fix date and use ::: syntax for code snippets as requested Co-authored-by: BillWagner <[email protected]> * Update docs/fsharp/language-reference/query-expressions.md * Revert db.Student renaming and fix date to 2025 as requested Co-authored-by: T-Gro <[email protected]> * Replace in-memory examples with Entity Framework Core as requested Co-authored-by: T-Gro <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: BillWagner <[email protected]> Co-authored-by: Bill Wagner <[email protected]> Co-authored-by: T-Gro <[email protected]>
1 parent 0b4a3ee commit 0de9e89

File tree

6 files changed

+341
-58
lines changed

6 files changed

+341
-58
lines changed

docs/fsharp/language-reference/query-expressions.md

Lines changed: 71 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
---
22
title: Query Expressions
33
description: Learn about query expression support for LINQ in the F# programming language.
4-
ms.date: 08/15/2020
4+
ms.date: 09/26/2025
5+
ai-usage: ai-assisted
56
---
67
# Query expressions
78

@@ -15,27 +16,9 @@ query { expression }
1516

1617
## Remarks
1718

18-
Query expressions are a type of computation expression similar to sequence expressions. Just as you specify a sequence by providing code in a sequence expression, you specify a set of data by providing code in a query expression. In a sequence expression, the `yield` keyword identifies data to be returned as part of the resulting sequence. In query expressions, the `select` keyword performs the same function. In addition to the `select` keyword, F# also supports a number of query operators that are much like the parts of a SQL SELECT statement. Here is an example of a simple query expression, along with code that connects to the Northwind OData source.
19+
Query expressions are a type of computation expression similar to sequence expressions. Just as you specify a sequence by providing code in a sequence expression, you specify a set of data by providing code in a query expression. In a sequence expression, the `yield` keyword identifies data to be returned as part of the resulting sequence. In query expressions, the `select` keyword performs the same function. In addition to the `select` keyword, F# also supports a number of query operators that are much like the parts of a SQL SELECT statement. Here is an example of a simple query expression using Entity Framework Core to query a database.
1920

20-
```fsharp
21-
// Use the OData type provider to create types that can be used to access the Northwind database.
22-
// Add References to FSharp.Data.TypeProviders and System.Data.Services.Client
23-
open Microsoft.FSharp.Data.TypeProviders
24-
25-
type Northwind = ODataService<"http://services.odata.org/Northwind/Northwind.svc">
26-
let db = Northwind.GetDataContext()
27-
28-
// A query expression.
29-
let query1 =
30-
query {
31-
for customer in db.Customers do
32-
select customer
33-
}
34-
35-
// Print results
36-
query1
37-
|> Seq.iter (fun customer -> printfn "Company: %s Contact: %s" customer.CompanyName customer.ContactName)
38-
```
21+
:::code language="fsharp" source="~/samples/snippets/fsharp/query-expressions/snippet1.fs":::
3922

4023
In the previous code example, the query expression is in curly braces. The meaning of the code in the expression is, return every customer in the Customers table in the database in the query results. Query expressions return a type that implements <xref:System.Linq.IQueryable%601> and <xref:System.Collections.Generic.IEnumerable%601>, and so they can be iterated using the [Seq module](https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-seqmodule.html) as the example shows.
4124

@@ -53,22 +36,9 @@ This table assumes a database in the following form:
5336

5437
![Diagram that shows a sample database.](./media/query-expressions/student-course-database.png)
5538

56-
The code in the tables that follow also assumes the following database connection code. Projects should add references to System.Data, System.Data.Linq, and FSharp.Data.TypeProviders assemblies. The code that creates this database is included at the end of this topic.
57-
58-
```fsharp
59-
open System
60-
open Microsoft.FSharp.Data.TypeProviders
61-
open System.Data.Linq.SqlClient
62-
open System.Linq
63-
open Microsoft.FSharp.Linq
64-
65-
type schema = SqlDataConnection< @"Data Source=SERVER\INSTANCE;Initial Catalog=MyDatabase;Integrated Security=SSPI;" >
66-
67-
let db = schema.GetDataContext()
39+
The code in the tables that follow also assumes the following database connection code using Entity Framework Core. Projects should add package references to `Microsoft.EntityFrameworkCore` and `Microsoft.EntityFrameworkCore.InMemory` (or another EF Core provider for production scenarios). This example uses the in-memory database provider for demonstration purposes, but the same query syntax works with any EF Core provider (SQL Server, PostgreSQL, etc.).
6840

69-
// Needed for some query operator examples:
70-
let data = [ 1; 5; 7; 11; 18; 21]
71-
```
41+
:::code language="fsharp" source="~/samples/snippets/fsharp/query-expressions/snippet2.fs":::
7242

7343
### Table 1. Query Operators
7444

@@ -84,6 +54,7 @@ let data = [ 1; 5; 7; 11; 18; 21]
8454
```fsharp
8555
query {
8656
for student in db.Student do
57+
where student.Age.IsSome
8758
select student.Age.Value
8859
contains 11
8960
}
@@ -242,7 +213,7 @@ query {
242213
query {
243214
for student in db.Student do
244215
where student.Age.HasValue
245-
sortBy student.Age.Value
216+
sortByNullable student.Age
246217
thenBy student.Name
247218
select student
248219
}
@@ -255,7 +226,7 @@ query {
255226
query {
256227
for student in db.Student do
257228
where student.Age.HasValue
258-
sortBy student.Age.Value
229+
sortByNullable student.Age
259230
thenByDescending student.Name
260231
select student
261232
}
@@ -1350,19 +1321,71 @@ VALUES(15, 7, 3);
13501321
The following code contains the sample code that appears in this topic.
13511322

13521323
```fsharp
1353-
#if INTERACTIVE
1354-
#r "FSharp.Data.TypeProviders.dll"
1355-
#r "System.Data.dll"
1356-
#r "System.Data.Linq.dll"
1357-
#endif
13581324
open System
1359-
open Microsoft.FSharp.Data.TypeProviders
1360-
open System.Data.Linq.SqlClient
13611325
open System.Linq
13621326
1363-
type schema = SqlDataConnection<"Data Source=SERVER\INSTANCE;Initial Catalog=MyDatabase;Integrated Security=SSPI;">
1364-
1365-
let db = schema.GetDataContext()
1327+
// Define simple data types to represent our sample database
1328+
type Student = {
1329+
StudentID: int
1330+
Name: string
1331+
Age: int option
1332+
}
1333+
1334+
type Course = {
1335+
CourseID: int
1336+
CourseName: string
1337+
}
1338+
1339+
type CourseSelection = {
1340+
ID: int
1341+
StudentID: int
1342+
CourseID: int
1343+
}
1344+
1345+
// Sample data
1346+
let students = [
1347+
{ StudentID = 1; Name = "Abercrombie, Kim"; Age = Some 10 }
1348+
{ StudentID = 2; Name = "Abolrous, Hazen"; Age = Some 14 }
1349+
{ StudentID = 3; Name = "Hance, Jim"; Age = Some 12 }
1350+
{ StudentID = 4; Name = "Adams, Terry"; Age = Some 12 }
1351+
{ StudentID = 5; Name = "Hansen, Claus"; Age = Some 11 }
1352+
{ StudentID = 6; Name = "Penor, Lori"; Age = Some 13 }
1353+
{ StudentID = 7; Name = "Perham, Tom"; Age = Some 12 }
1354+
{ StudentID = 8; Name = "Peng, Yun-Feng"; Age = None }
1355+
]
1356+
1357+
let courses = [
1358+
{ CourseID = 1; CourseName = "Algebra I" }
1359+
{ CourseID = 2; CourseName = "Trigonometry" }
1360+
{ CourseID = 3; CourseName = "Algebra II" }
1361+
{ CourseID = 4; CourseName = "History" }
1362+
{ CourseID = 5; CourseName = "English" }
1363+
{ CourseID = 6; CourseName = "French" }
1364+
{ CourseID = 7; CourseName = "Chinese" }
1365+
]
1366+
1367+
let courseSelections = [
1368+
{ ID = 1; StudentID = 1; CourseID = 2 }
1369+
{ ID = 2; StudentID = 1; CourseID = 3 }
1370+
{ ID = 3; StudentID = 1; CourseID = 5 }
1371+
{ ID = 4; StudentID = 2; CourseID = 2 }
1372+
{ ID = 5; StudentID = 2; CourseID = 5 }
1373+
{ ID = 6; StudentID = 2; CourseID = 6 }
1374+
{ ID = 7; StudentID = 2; CourseID = 3 }
1375+
{ ID = 8; StudentID = 3; CourseID = 2 }
1376+
{ ID = 9; StudentID = 3; CourseID = 1 }
1377+
{ ID = 10; StudentID = 4; CourseID = 2 }
1378+
{ ID = 11; StudentID = 4; CourseID = 5 }
1379+
{ ID = 12; StudentID = 4; CourseID = 2 }
1380+
{ ID = 13; StudentID = 5; CourseID = 3 }
1381+
{ ID = 14; StudentID = 5; CourseID = 2 }
1382+
{ ID = 15; StudentID = 7; CourseID = 3 }
1383+
]
1384+
1385+
// Convert to queryable collections for LINQ operations
1386+
let db.Student = students.AsQueryable()
1387+
let coursesQueryable = courses.AsQueryable()
1388+
let courseSelectionsQueryable = courseSelections.AsQueryable()
13661389
13671390
let data = [1; 5; 7; 11; 18; 21]
13681391
@@ -1984,15 +2007,7 @@ query {
19842007
And here is the full output when this code is run in F# Interactive.
19852008

19862009
```console
1987-
--> Referenced 'C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp\3.0\Runtime\v4.0\Type Providers\FSharp.Data.TypeProviders.dll'
1988-
1989-
--> Referenced 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Data.dll'
1990-
1991-
--> Referenced 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Data.Linq.dll'
1992-
19932010
contains query operator
1994-
Binding session to 'C:\Users\ghogen\AppData\Local\Temp\tmp5E3C.dll'...
1995-
Binding session to 'C:\Users\ghogen\AppData\Local\Temp\tmp611A.dll'...
19962011
Is at least one student age 11? true
19972012

19982013
count query operator
Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
1-
<Project Sdk="Microsoft.Net.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net8.0</TargetFramework>
5+
<TargetFramework>net9.0</TargetFramework>
66
<LangVersion>preview</LangVersion>
77
</PropertyGroup>
88

99
<ItemGroup>
1010
<Compile Include="tour.fs" />
11+
<None Include="query-expressions/basic-query.fs" />
12+
<None Include="query-expressions/modern-example.fs" />
13+
<None Include="query-expressions/snippet1.fs" />
14+
<None Include="query-expressions/snippet2.fs" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
19+
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.0" />
1120
</ItemGroup>
1221

1322
</Project>
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
module QueryExpressions.BasicQuery
2+
3+
// Basic query expression example using in-memory data
4+
open System
5+
open System.Linq
6+
7+
// Define simple data types to represent our sample database
8+
type Student = {
9+
StudentID: int
10+
Name: string
11+
Age: int option
12+
}
13+
14+
type Course = {
15+
CourseID: int
16+
CourseName: string
17+
}
18+
19+
type CourseSelection = {
20+
ID: int
21+
StudentID: int
22+
CourseID: int
23+
}
24+
25+
// Sample data
26+
let students = [
27+
{ StudentID = 1; Name = "Abercrombie, Kim"; Age = Some 10 }
28+
{ StudentID = 2; Name = "Abolrous, Hazen"; Age = Some 14 }
29+
{ StudentID = 3; Name = "Hance, Jim"; Age = Some 12 }
30+
{ StudentID = 4; Name = "Adams, Terry"; Age = Some 12 }
31+
{ StudentID = 5; Name = "Hansen, Claus"; Age = Some 11 }
32+
{ StudentID = 6; Name = "Penor, Lori"; Age = Some 13 }
33+
{ StudentID = 7; Name = "Perham, Tom"; Age = Some 12 }
34+
{ StudentID = 8; Name = "Peng, Yun-Feng"; Age = None }
35+
]
36+
37+
let courses = [
38+
{ CourseID = 1; CourseName = "Algebra I" }
39+
{ CourseID = 2; CourseName = "Trigonometry" }
40+
{ CourseID = 3; CourseName = "Algebra II" }
41+
{ CourseID = 4; CourseName = "History" }
42+
{ CourseID = 5; CourseName = "English" }
43+
{ CourseID = 6; CourseName = "French" }
44+
{ CourseID = 7; CourseName = "Chinese" }
45+
]
46+
47+
let courseSelections = [
48+
{ ID = 1; StudentID = 1; CourseID = 2 }
49+
{ ID = 2; StudentID = 1; CourseID = 3 }
50+
{ ID = 3; StudentID = 1; CourseID = 5 }
51+
{ ID = 4; StudentID = 2; CourseID = 2 }
52+
{ ID = 5; StudentID = 2; CourseID = 5 }
53+
{ ID = 6; StudentID = 2; CourseID = 6 }
54+
{ ID = 7; StudentID = 2; CourseID = 3 }
55+
{ ID = 8; StudentID = 3; CourseID = 2 }
56+
{ ID = 9; StudentID = 3; CourseID = 1 }
57+
{ ID = 10; StudentID = 4; CourseID = 2 }
58+
{ ID = 11; StudentID = 4; CourseID = 5 }
59+
{ ID = 12; StudentID = 4; CourseID = 2 }
60+
{ ID = 13; StudentID = 5; CourseID = 3 }
61+
{ ID = 14; StudentID = 5; CourseID = 2 }
62+
{ ID = 15; StudentID = 7; CourseID = 3 }
63+
]
64+
65+
// Convert to queryable collections for LINQ operations
66+
let studentsQueryable = students.AsQueryable()
67+
let coursesQueryable = courses.AsQueryable()
68+
let courseSelectionsQueryable = courseSelections.AsQueryable()
69+
70+
// A query expression example
71+
let query1 =
72+
query {
73+
for student in studentsQueryable do
74+
select student
75+
}
76+
77+
// Print results
78+
query1
79+
|> Seq.iter (fun student -> printfn "Student: %s" student.Name)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module QueryExpressions.ModernExample
2+
3+
// Modern F# query expression example using in-memory collections
4+
// This provides a working alternative to deprecated type providers
5+
6+
open System
7+
open System.Linq
8+
9+
// Simple data structures to demonstrate query capabilities
10+
type Customer = {
11+
CustomerID: int
12+
CompanyName: string
13+
ContactName: string
14+
}
15+
16+
// Sample data that replaces the external data source
17+
let customers = [
18+
{ CustomerID = 1; CompanyName = "Alfreds Futterkiste"; ContactName = "Maria Anders" }
19+
{ CustomerID = 2; CompanyName = "Ana Trujillo Emparedados y helados"; ContactName = "Ana Trujillo" }
20+
{ CustomerID = 3; CompanyName = "Antonio Moreno Taquería"; ContactName = "Antonio Moreno" }
21+
{ CustomerID = 4; CompanyName = "Around the Horn"; ContactName = "Thomas Hardy" }
22+
{ CustomerID = 5; CompanyName = "Berglunds snabbköp"; ContactName = "Christina Berglund" }
23+
]
24+
25+
// Convert to queryable for LINQ operations
26+
let db = customers.AsQueryable()
27+
28+
// A query expression that works with modern .NET
29+
let query1 =
30+
query {
31+
for customer in db do
32+
select customer
33+
}
34+
35+
// Print results (this would be equivalent to the old example)
36+
let printResults() =
37+
query1
38+
|> Seq.iter (fun customer -> printfn "Company: %s Contact: %s" customer.CompanyName customer.ContactName)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// F# query expression example using Entity Framework Core
2+
open System
3+
open System.Linq
4+
open Microsoft.EntityFrameworkCore
5+
6+
// Entity type
7+
[<CLIMutable>]
8+
type Customer = {
9+
CustomerID: int
10+
CompanyName: string
11+
ContactName: string
12+
}
13+
14+
// Database context
15+
type NorthwindContext() =
16+
inherit DbContext()
17+
18+
[<DefaultValue>]
19+
val mutable private customers: DbSet<Customer>
20+
member this.Customers with get() = this.customers and set v = this.customers <- v
21+
22+
override _.OnConfiguring(optionsBuilder: DbContextOptionsBuilder) =
23+
optionsBuilder.UseInMemoryDatabase("NorthwindDatabase") |> ignore
24+
25+
// Create and seed database
26+
let db =
27+
let context = new NorthwindContext()
28+
context.Customers.AddRange([|
29+
{ CustomerID = 1; CompanyName = "Alfreds Futterkiste"; ContactName = "Maria Anders" }
30+
{ CustomerID = 2; CompanyName = "Ana Trujillo Emparedados y helados"; ContactName = "Ana Trujillo" }
31+
{ CustomerID = 3; CompanyName = "Antonio Moreno Taquería"; ContactName = "Antonio Moreno" }
32+
|]) |> ignore
33+
context.SaveChanges() |> ignore
34+
context
35+
36+
// A query expression
37+
let query1 =
38+
query {
39+
for customer in db.Customers do
40+
select customer
41+
}
42+
43+
// Print results
44+
query1
45+
|> Seq.iter (fun customer -> printfn "Company: %s Contact: %s" customer.CompanyName customer.ContactName)

0 commit comments

Comments
 (0)