Skip to content

Commit

Permalink
Merge pull request #353 from SAFE-Stack/dependency-injection
Browse files Browse the repository at this point in the history
Add DI recipe
  • Loading branch information
isaacabraham authored Jan 12, 2024
2 parents cd2711f + 9929337 commit f574d44
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 1 deletion.
54 changes: 54 additions & 0 deletions docs/recipes/patterns/add-dependency-injection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
> This recipe is not a detailed discussion of the pros and cons of Dependency Injection (DI) compared to other patterns. It simply illustrates how to use it within a SAFE Stack application!
1. Create a class that you wish to inject with a dependency (in this example, we use the built-in `IConfiguration` type that is included in ASP .NET):

```fsharp
open Microsoft.Extensions.Configuration
type DatabaseRepository(config:IConfiguration) =
member _.SaveDataToDb (text:string) =
let connectionString = config["SqlConnectionString"]
// Connect to SQL using the above connection string etc.
Ok 1
```
> Instead of functions or modules, DI in .NET and F# only works with classes.
2. Register your type with ASP .NET during startup within the `application { }` block.
```diff
++ open Microsoft.Extensions.DependencyInjection
application {
//...
++ service_config (fun services -> services.AddSingleton<DatabaseRepository>())
```
> [This section](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-8.0#lifetime-and-registration-options) of the official ASP .NET Core article explain the distinction between different lifetime registrations, such as Singleton and Transient.
3. Ensure that your Fable Remoting API can access the `HttpContext` type by using the `fromContext` builder function.
```diff
-- |> Remoting.fromValue createFableRemotingApi
++ |> Remoting.fromContext createFableRemotingApi
```
4. Within your Fable Remoting API, use the supplied `context` to retrieve your dependency:
```diff
++ open Microsoft.AspNetCore.Http
let createFableRemotingApi
++ (context:HttpContext) =
++ let dbRepository = context.GetService<DatabaseRepository>()
// ...
// Return the constructed API record value...
```
> Giraffe provides the `GetService<'T>` extension to allow you to quickly retrieve a dependency from the `HttpContext`.
This will instruct ASP .NET to get a handle to the `DatabaseRepository` object; ASP .NET will automatically supply the `IConfiguration` object to the constructor. Whether a new `DatabaseRepository` object is constructed on each call depends on the lifetime you have registered it with.
> You can have your types depend on other types that you create, as long as they are registering into ASP .NET Core's DI container using methods such as `AddSingleton` etc.
## Further Reading
* [Official documentation on DI in ASP .NET Core](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-8.0)
* [Archived example PR to update the SAFE Template Todo App to use DI](https://github.com/SAFE-Stack/SAFE-template/pull/466/files)
4 changes: 3 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ extra:
analytics:
provider: google
property: G-WED2S9FLSL

nav:
- Home: "index.md"
- Introduction: "intro.md"
Expand Down Expand Up @@ -107,6 +107,8 @@ nav:
- Migrate to Paket from NuGet: "recipes/package-management/migrate-to-paket.md"
- Migrate to NuGet from Paket: "recipes/package-management/migrate-to-nuget.md"
- Sync NuGet and NPM Packages: "recipes/package-management/sync-nuget-and-npm-packages.md"
- Patterns:
- Use Dependency Injection: "recipes/patterns/add-dependency-injection.md"
- Client / Server:
- Use Giraffe instead of Saturn: "recipes/client-server/saturn-to-giraffe.md"
- Handle server errors on the client: "recipes/client-server/server-errors-on-client.md"
Expand Down

0 comments on commit f574d44

Please sign in to comment.