Skip to content

Commit

Permalink
chore: update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewTriesToCode committed Mar 16, 2024
1 parent 2050040 commit 734db0f
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 42 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# added
.vscode/
*.db
*.db-shm
*.db-wal

# globs
Makefile.in
Expand Down
84 changes: 42 additions & 42 deletions docs/Options.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,37 @@
# Per-Tenant Options

Finbuckle.MultiTenant is designed to emphasize using per-tenant options in an app to drive per-tenant behavior. This
approach allows app logic to be written having to add tenant-dependent or
tenant-specific logic to the code.

By using per-tenant options, the options values used within app logic will automatically
reflect the per-tenant values as configured for the current tenant. Any code already using the Options pattern will gain
multi-tenant capability with minimal code changes.

Finbuckle.MultiTenant integrates with the
standard [.NET Options pattern](https://learn.microsoft.com/en-us/dotnet/core/extensions/options) (see also the [ASP.NET
Core Options pattern](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options) and lets apps
customize options distinctly for each tenant.

Note: For authentication options, Finbuckle.MultiTenant provides special support
for [per-tenant authentication](Authentication).

The current tenant determines which options are retrieved via
the `IOptions<TOptions>`, `IOptionsSnapshot<TOptions>`, or `IOptionsMonitor<TOptions>` instances' `Value` property and
`Get(string name)` method.

A specialized variation of this is [per-tenant authentication](Authentication).

Per-tenant options will work with *any* options class when using `IOptions<TOptions>`, `IOptionsSnapshot<TOptions>`,
or `IOptionsMonitor<TOptions>` with dependency injection or service resolution. This includes an app's own code *and*
code internal to ASP.NET Core or other libraries that use the Options pattern. There is a caveat to be aware of: some
code, such as certain classes in ASP.NET Core or other libraries, may internally cache options resulting in only the
values from the first tenant being used despite the current tenant.
code internal to ASP.NET Core or other libraries that use the Options pattern.

A potential issue arises when code internally stores or caches options values from
an `IOptions<TOptions>`, `IOptionsSnapshot<TOptions>`, or `IOptionsMonitor<TOptions>` instance. This is usually
unnecessary because the options are already cached within the .NET options infrastructure, and in these cases the
initial instance of the options is always used, regardless of the current tenant. Finbuckle.MultiTenant works around
this for some parts of
ASP.NET Core, and recommends that in your own code to always access options values via
the `IOptions<TOptions>`, `IOptionsSnapshot<TOptions>`, or `IOptionsMonitor<TOptions>` instance. This will ensure the
correct values for the current tenant are used.

## Options Basics

Expand Down Expand Up @@ -62,20 +78,19 @@ public MyController : Controller
httpContext.RequestServices.GetServices<IOptionsSnaption<MyOptions>();
```

At this point the options would be the same for all tenants.
With standard options each tenant would get see the same exact options.

## Customizing Options Per Tenant

This sections assumes Finbuckle.MultiTenant is installed and configured with a `TTenantInfo` type of `TenantInfo`.
This sections assumes a standard web application builder is configured and Finbuckle.MultiTenant is configured with
a `TTenantInfo` type of `TenantInfo`.
See [Getting Started](GettingStarted) for details.

To configure options per tenant, the standard `Configure` method variants on the service collection now all
have `PerTenant` equivalents which accept a `Action<TOptions, TTenantInfo>` delegate. When the options are created at
runtime the delegate will be called with the current tenant details.

```cs
var builder = WebApplication.CreateBuilder(args);

// configure options per tenant
builder.Services.ConfigurePerTenant<MyOptions, Tenantnfo>((options, tenantInfo) =>
{
Expand Down Expand Up @@ -132,42 +147,27 @@ public MyController : Controller
}
```

## Named Options

You can configure options by name using the `WithPerTenantNamedOptions<TOptions>` method.

Call `WithPerTenantNamedOptions<TOptions>` after `AddMultiTenant<T>` in the `ConfigureServices` method:

```cs
services.AddMultiTenant<MyTenantInfo>()...
.WithPerTenantNamedOptions<MyOptions>(someOptionsName, (options, tenantInfo) =>
{
// only update options named "someOptionsName"
options.MyOption1 = tenantInfo.Option1Value;
options.MyOption2 = tenantInfo.Option2Value;
});
## Using the OptionsBuilder API

.NET provides
the [OptionsBuilder](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-8.0#optionsbuilder-api)
API to provide more flexibility for configuring options. This pattern simplifies dependency injection and validation for
the standard [Options pattern](https://learn.microsoft.com/en-us/dotnet/core/extensions/options). Finbuckle.MultiTenant
extends this API to enable options configuration for per-tenant options similarly. Note that while the `OptionsBuilder`
normally supports up to five dependencies, Finbuckle.MultiTenant support only supports four.

```csharp
// use OptionsBuilder API to configure per-tenant options with dependencies
builder.Services
.AddOptions<MyOptions>("optionalName")
.ConfigurePerTenant<ExampleService, TenantInfo>(
(options, es, tenantInfo) =>
options.Property = DoSomethingWith(es, tenantInfo));
```

The `string` parameter is the name of the options. The type parameter `TOptions` is the options type being customized
per-tenant. The method parameter is an `Action<string, TOptions, TenantInfo>`. This action will modify the options
instance *after* the options normal configuration and *before*
its [post configuration](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?#ipostconfigureoptions)
.

`WithPerTenantNameOptions<TOptions>` can be called multiple times on the same `TOptions`
type and the configuration will run in the respective order.

The same delegate passed to `WithPerTenantNameOptions<TOptions>` is applied to all options generated of type `TOptions`
regardless of the option name. You can use the `name` argument in the callback to help you set the correct options by
name.

`WithPerTenantOptions<TOptions>` can be used in combination with `WithPerTenantNameOptions<TOptions>` for the same
type `TOptions`. The `WithPerTenantOptions<TOptions>` callbacks will be invoked first, followed by
the `WithPerTenantNameOptions<TOptions>` callbacks.

## Options Caching
## Options and Caching

Internally ASP.NET Core caches options, and Finbuckle.MultiTenant extends this to cache options per tenant. Caching
Internally .NET caches options, and Finbuckle.MultiTenant extends this to cache options per tenant. Caching
occurs when a `TOptions` instance is retrieved via `Value` or `Get` on the injected `IOptions<TOptions>` (or derived)
instance for the first time for a tenant.

Expand Down

0 comments on commit 734db0f

Please sign in to comment.