Skip to content

Commit 9891241

Browse files
authored
Improved 'Select Builders' docs.
1 parent efc461c commit 9891241

File tree

1 file changed

+28
-44
lines changed

1 file changed

+28
-44
lines changed

README.md

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -344,18 +344,41 @@ let getErrorNumbers () =
344344
```
345345

346346
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`
350356

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.
353359

354360
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).
355361

356362
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:
357363
* `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!
358364
* `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+
359382

360383
Selecting city and state columns only:
361384
```F#
@@ -684,45 +707,6 @@ let getCities () =
684707
}
685708
```
686709

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
715-
716-
// Usage:
717-
718-
let! distinctCustomerNames =
719-
selectTask' (Create openContext) {
720-
for c in SalesLT.Customer do
721-
select (c.FirstName, c.LastName)
722-
distinct
723-
}
724-
```
725-
726710
### Insert Builder
727711

728712
#### Simple Inserts

0 commit comments

Comments
 (0)