Skip to content

Commit cd37e91

Browse files
authored
Merge pull request #341 from SAFE-Stack/v5-docs-using-sql-provider-sql-server-ssdt
V5 docs using sql provider sql server ssdt
2 parents a2cf0ee + 7d5f38c commit cd37e91

File tree

7 files changed

+241
-0
lines changed

7 files changed

+241
-0
lines changed

docs/img/sql-provider1.png

7.44 KB
Loading

docs/img/sql-provider2.png

31.9 KB
Loading

docs/img/sql-provider3.png

35.5 KB
Loading

docs/img/sql-provider4.png

28.8 KB
Loading

docs/img/sql-provider5.png

29.8 KB
Loading
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
# Using SQLProvider SQL Server SSDT
2+
3+
4+
## Set up your database Server using Docker
5+
6+
The easiest way to get a database running locally is using Docker. You can find the installer on their [website](https://www.docker.com/get-started/). Once docker is installed, use the following command to spin up a database server
7+
8+
```
9+
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=<your password>" -p 1433:1433 -d mcr.microsoft.com/mssql/server:2022-latest
10+
```
11+
12+
## Creating a "SafeTodo" Database with Azure Data Studio
13+
14+
15+
### Connecting to a SQL Server Instance
16+
1) In the "Connections" tab, click the "New Connection" button
17+
18+
![image](../../img/sql-provider1.png)
19+
20+
2) Enter your connection details, leaving the "Database" dropdown set to `<Default>`.
21+
22+
![image](../../img/sql-provider2.png)
23+
24+
### Creating a new "SafeTodo" Database
25+
- Right click your server and choose "New Query"
26+
- Execute this script:
27+
28+
```sql
29+
USE master
30+
GO
31+
IF NOT EXISTS (
32+
SELECT name
33+
FROM sys.databases
34+
WHERE name = N'SafeTodo'
35+
)
36+
CREATE DATABASE [SafeTodo];
37+
GO
38+
IF SERVERPROPERTY('ProductVersion') > '12'
39+
ALTER DATABASE [SafeTodo] SET QUERY_STORE=ON;
40+
GO
41+
42+
```
43+
44+
- Right-click the "Databases" folder and choose "Refresh" to see the new database.
45+
46+
_NOTE: Alternatively, if you don't want to manually create the new database, you can install the "New Database" extension in Azure Data Studio which gives you a "New Database" option when right clicking the "Databases" folder._
47+
48+
### Create a "Todos" Table
49+
- Right-click on the **SafeTodo** database and choose "New Query"
50+
- Execute this script:
51+
``` sql
52+
CREATE TABLE [dbo].[Todos]
53+
(
54+
[Id] UNIQUEIDENTIFIER NOT NULL PRIMARY KEY,
55+
[Description] NVARCHAR(500) NOT NULL,
56+
[IsDone] BIT NOT NULL
57+
)
58+
```
59+
60+
## Creating an SSDT Project (.sqlproj)
61+
At this point, you should have a SAFE Stack solution and a minimal "SafeTodo" SQL Server database with a "Todos" table.
62+
Next, we will use Azure Data Studio with the "SQL Database Projects" extension to create a new SSDT (SQL Server Data Tools) .sqlproj that will live in our SAFE Stack .sln.
63+
64+
1) Install the "SQL Database Projects" extension.
65+
66+
2) Right click the SafeTodo database and choose "Create Project From Database" (this option is added by the "SQL Database Projects" extension)
67+
68+
![image](../../img/sql-provider3.png)
69+
70+
3) Configure a path within your SAFE Stack solution folder and a project name and then click "Create". NOTE: If you choose to create an "ssdt" subfolder as I did, you will need to manually create this subfolder first.
71+
72+
![image](../../img/sql-provider4.png)
73+
74+
4) You should now be able to view your SQL Project by clicking the "Projects" tab in Azure Data Studio.
75+
76+
![image](../../img/sql-provider5.png)
77+
78+
5) Finally, right click the SafeTodoDB project and select "Build". This will create a .dacpac file which we will use in the next step.
79+
80+
81+
## Create a TodoRepository Using the new SSDT provider in SQLProvider
82+
83+
### Installing SQLProvider from NuGet
84+
85+
Install dependencies `SqlProvider` and `Microsoft.Data.SqlClient`
86+
87+
```
88+
dotnet paket add SqlProvider -p Server
89+
dotnet paket add Microsoft.Data.SqlClient -p Server
90+
```
91+
92+
### Initialize Type Provider
93+
Next, we will wire up our type provider to generate database types based on the compiled .dacpac file.
94+
95+
1) In the Server project, create a new file, `Database.fs`. (this should be above `Server.fs`).
96+
97+
```fsharp
98+
module Database
99+
open FSharp.Data.Sql
100+
101+
[<Literal>]
102+
let SsdtPath = __SOURCE_DIRECTORY__ + @"/../../ssdt/SafeTodoDB/bin/Debug/SafeTodoDB.dacpac"
103+
104+
type DB =
105+
SqlDataProvider<
106+
Common.DatabaseProviderTypes.MSSQLSERVER_SSDT,
107+
SsdtPath = SsdtPath,
108+
UseOptionTypes = Common.NullableColumnType.OPTION
109+
>
110+
111+
//TO RELOAD SCHEMA: 1) uncomment the line below; 2) save; 3) recomment; 4) save again and wait.
112+
//DB.GetDataContext().``Design Time Commands``.ClearDatabaseSchemaCache
113+
114+
let createContext (connectionString: string) =
115+
DB.GetDataContext(connectionString)
116+
```
117+
118+
2) Create `TodoRepository.fs` below `Database.fs`.
119+
120+
``` fsharp
121+
module TodoRepository
122+
open FSharp.Data.Sql
123+
open Database
124+
open Shared
125+
126+
/// Get all todos that have not been marked as "done".
127+
let getTodos (db: DB.dataContext) =
128+
query {
129+
for todo in db.Dbo.Todos do
130+
where (not todo.IsDone)
131+
select
132+
{ Shared.Todo.Id = todo.Id
133+
Shared.Todo.Description = todo.Description }
134+
}
135+
|> List.executeQueryAsync
136+
137+
let addTodo (db: DB.dataContext) (todo: Shared.Todo) =
138+
async {
139+
let t = db.Dbo.Todos.Create()
140+
t.Id <- todo.Id
141+
t.Description <- todo.Description
142+
t.IsDone <- false
143+
144+
do! db.SubmitUpdatesAsync() |> Async.AwaitTask
145+
}
146+
```
147+
148+
3) Create `TodoController.fs` below `TodoRepository.fs`.
149+
150+
``` fsharp
151+
module TodoController
152+
open Database
153+
open Shared
154+
155+
let getTodos (db: DB.dataContext) =
156+
TodoRepository.getTodos db |> Async.AwaitTask
157+
158+
let addTodo (db: DB.dataContext) (todo: Todo) =
159+
async {
160+
if Todo.isValid todo.Description then
161+
do! TodoRepository.addTodo db todo
162+
return todo
163+
else
164+
return failwith "Invalid todo"
165+
}
166+
```
167+
168+
4) Finally, replace the stubbed todosApi implementation in `Server.fs` with our type provided implementation.
169+
170+
``` fsharp
171+
module Server
172+
173+
open Fable.Remoting.Server
174+
open Fable.Remoting.Giraffe
175+
open Saturn
176+
open System
177+
open Shared
178+
open Microsoft.AspNetCore.Http
179+
180+
let todosApi =
181+
let db = Database.createContext @"Data Source=localhost,1433;Database=SafeTodo;User ID=sa;Password=<your password>;TrustServerCertificate=True"
182+
{ getTodos = fun () -> TodoController.getTodos db
183+
addTodo = TodoController.addTodo db }
184+
185+
let webApp =
186+
Remoting.createApi()
187+
|> Remoting.withRouteBuilder Route.builder
188+
|> Remoting.fromValue todosApi
189+
|> Remoting.withErrorHandler fableRemotingErrorHandler
190+
|> Remoting.buildHttpHandler
191+
192+
let app =
193+
application {
194+
use_router webApp
195+
memory_cache
196+
use_static "public"
197+
use_gzip
198+
}
199+
200+
run app
201+
```
202+
203+
## Run the App!
204+
From the VS Code terminal in the SafeTodo folder, launch the app (server and client):
205+
206+
`dotnet run`
207+
208+
You should now be able to add todos.
209+
210+
![image](https://user-images.githubusercontent.com/1030435/111055048-044f2080-8440-11eb-9efc-ae454ff071c4.png)
211+
212+
## Deployment
213+
When creating a Release build for deployment, it is important to note that SQLProvider SSDT expects that the .dacpac file will be copied to the deployed Server project bin folder.
214+
215+
Here are the steps to accomplish this:
216+
217+
1) Modify your Server.fsproj to include the .dacpac file with "CopyToOutputDirectory" to ensure that the .dacpac file will always exist in the Server project bin folder.
218+
219+
```
220+
<ItemGroup>
221+
<None Include="..\{relative path to SSDT project}\ssdt\SafeTodo\bin\$(Configuration)\SafeTodoDB.dacpac" Link="SafeTodoDB.dacpac">
222+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
223+
</None>
224+
225+
{ other files... }
226+
</ItemGroup>
227+
```
228+
229+
2) In your Server.Database.fs file, you should also modify the SsdtPath binding so that it can build the project in either Debug or Release mode:
230+
231+
```F#
232+
[<Literal>]
233+
#if DEBUG
234+
let SsdtPath = __SOURCE_DIRECTORY__ + @"/../../ssdt/SafeTodoDB/bin/Debug/SafeTodoDB.dacpac"
235+
#else
236+
let SsdtPath = __SOURCE_DIRECTORY__ + @"/../../ssdt/SafeTodoDB/bin/Release/SafeTodoDB.dacpac"
237+
#endif
238+
```
239+
240+
NOTE: This assumes that your SSDT .sqlproj will be built in Release mode (you can build it manually, or use a FAKE build script to handle this).

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ nav:
9696
- Add Routing with UseElmish: "recipes/ui/routing-with-elmish.md"
9797
- Storage:
9898
- Quickly add a database: "recipes/storage/use-litedb.md"
99+
- Create a data module using SQLProvider SQL Server SSDT: "recipes/storage/use-sqlprovider-ssdt.md"
99100
- JavaScript:
100101
- Import a JavaScript module: "recipes/javascript/import-js-module.md"
101102
- Add Support for a Third Party React Library: "recipes/javascript/third-party-react-package.md"

0 commit comments

Comments
 (0)