You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+28-44Lines changed: 28 additions & 44 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -344,18 +344,41 @@ let getErrorNumbers () =
344
344
```
345
345
346
346
Which one should you use?
347
-
* The `select` computation expression is initially the most straightforward to use, but it should _always_ be wrapped within either an `async` or `task` computation expression, which results in more lines of code.
348
-
* The `selectTask` computation expression is self-executing and does not require being wrapped within an `async` or `task` computation expression. It takes a `QueryType` argument that lets you control whether a `QueryContext` is `Shared` or `Created`.
349
-
* The `selectAsync` computation expression is self-executing and does not require being wrapped within an `async` or `task` computation expression. It takes a `QueryType` argument that lets you control whether a `QueryContext` is `Shared` or `Created`.
347
+
* The `select` computation expression is simple and straightforward to use, but it should _always_ be wrapped within either an `async` or `task` computation expression, which results in more lines of code.
348
+
* The `selectTask` and `selectAsync` computation expressions are self-executing and do not require being wrapped within an `async` or `task` computation expression. They also take a `QueryType` argument that lets you control whether a `QueryContext` is `Shared` or `Created`.
349
+
* The `selectTask` and `selectAsync` computation expressions builders also provide the following custom operations that are applied to the queried results (after the query data is returned):
350
+
*`toArray`
351
+
*`toList`
352
+
*`mapArray`
353
+
*`mapList`
354
+
*`tryHead`
355
+
*`head`
350
356
351
-
So, if the `selectTask` and `selectAsync` CEs seem intimidating or too complex for your taste, then feel free to use the `select` CE.
352
-
However, the `selectTask` and `selectAsync` CEs are self-executing and often result in less lines of code.
357
+
For these reasons, I prefer using the `selectTask` and `selectAsync` CEs.
358
+
However, if the `selectTask` and `selectAsync` CEs seem intimidating or too complex for your taste, then feel free to use the `select` CE.
353
359
354
360
Note that all three select query builders require the generated `HydraReader.Read` static method (which is generated by `SqlHydra.Cli` when the ["Generate HydraReader?"](#data-readers) option is selected).
355
361
356
362
The `selectTask` and `selectAsync` builders also require a `QueryType` argument which is a discriminated union that allows the user to specify the scope of the `QueryContext` (which manages the `DbConnection` and executes the various types of queries). `QueryType` allows for the following options:
357
363
*`QueryType.Create of unit -> QueryContext` - this takes a function that returns a new `QueryContext`. This option will create its own `QueryContext` and `DbConnection` automatically, execute the query and then dispose them. This is very useful because it allows you to create a simple data function that executes a query without the need of manually instantiating the `QueryContext`, executing the query and then disposing (which also necessitates wrapping everything in a `task` or `async` block to ensure that the connection isn't prematurely disposed). The end result is a much cleaner data function that doesn't need to be wrapped in a `task` or `async` block!
358
364
*`QueryType.Shared of QueryContext` - this takes an already instantiated `QueryContext` and uses it to execute the query. In this case, the builder will ensure that the connection is open before executing the query, but it will not try to close or dispose when it is done. This is useful for when you need to call multiple queries within a `task` or `async` block with a single shared `QueryContext`.
365
+
366
+
### Creating a Custom `selectAsync` or `selectTask` Builder
367
+
If the redundancy of passing the generated `HydraReader.Read` static method into the `selectAsync` and `selectTask` builders bothers you, you can easily create your builder that has it baked-in:
368
+
369
+
```F#
370
+
let selectTask' ct = selectTask HydraReader.Read ct
371
+
372
+
// Usage:
373
+
374
+
let! distinctCustomerNames =
375
+
selectTask' (Create openContext) {
376
+
for c in SalesLT.Customer do
377
+
select (c.FirstName, c.LastName)
378
+
distinct
379
+
}
380
+
```
381
+
359
382
360
383
Selecting city and state columns only:
361
384
```F#
@@ -684,45 +707,6 @@ let getCities () =
684
707
}
685
708
```
686
709
687
-
### Using the `selectAsync` and `selectTask` Builders
688
-
The new `selectAsync` and `selectTask` builders should generally be prefered over the older `select` builder (with the exception of creating subqueries, which must be done using the `select` builder) because they provide several advantages:
689
-
690
-
#### They are self-executing
691
-
The new `selectAsync` and `selectTask` builders will execute the query automatically, whereas the old `select` builder creates a query that must be manually passed into a `QueryContext` execution method.
692
-
693
-
#### They offer more explicit control over the `QueryContext` and connection handling
694
-
The new `selectAsync` and `selectTask` builders must be initialized with with a `ContextType` discriminated union value that can either be `Shared` or `Create`.
695
-
Passing in a `Shared` context will run the query with an already existing `QueryContext`, whereas `Create` will create a new context and dispose it automatically after executing the query.
696
-
697
-
#### They make it possible to create a query function that is not wrapped in an `async` or `task` block.
698
-
One problem with the `select` builder is that the `QueryContext` generally had to be initialized within the `task` block to ensure that it was not disposed while the task was running asynchronously. Having the ability to initilize a `selectAsync` or `selectTask` builder with `Create` makes it a completely self-contained query which can exist by itself in a function without being wrapped in a `task` block.
699
-
**The new `selectAsync` and `selectTask` builders have the following new custom operations that are applied to the queried results:**
700
-
701
-
*`toArray`
702
-
*`toList`
703
-
*`mapArray`
704
-
*`mapList`
705
-
These new operations are designed to make the new select builders completely self-contained by removing the need to pipe the results.
706
-
707
-
#### They are Cleaner
708
-
Removing the need to pipeline the query builder into a `QueryContext` makes the code a bit more tidy.
709
-
710
-
### Creating a Custom `selectAsync` or `selectTask` Builder
711
-
If the redundancy of passing the generated `HydraReader.Read` static method into the `selectAsync` and `selectTask` builders bothers you, you can easily create your builder that has it baked-in:
712
-
713
-
```F#
714
-
let selectTask' ct = selectTask HydraReader.Read ct
0 commit comments