This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Advanced uses of the Dapr pluggable components .NET SDK

How to use advanced techniques with with the Dapr pluggable components .NET SDK

While not typically needed by most, these guides show advanced ways to can configure your .NET pluggable components.

1 - Application Environment of a .NET Dapr pluggable component

How to configure the environment of a .NET pluggable component

A .NET Dapr pluggable component application can be configured for dependency injection, logging, and configuration values similarly to ASP.NET applications. The DaprPluggableComponentsApplication exposes a similar set of configuration properties to that exposed by WebApplicationBuilder.

Dependency injection

Components registered with services can participate in dependency injection. Arguments in the components constructor will be injected during creation, assuming those types have been registered with the application. You can register them through the IServiceCollection exposed by DaprPluggableComponentsApplication.

var app = DaprPluggableComponentsApplication.Create();

// Register MyService as the singleton implementation of IService.
app.Services.AddSingleton<IService, MyService>();

app.RegisterService(
    "<service name>",
    serviceBuilder =>
    {
        serviceBuilder.RegisterStateStore<MyStateStore>();
    });

app.Run();

interface IService
{
    // ...
}

class MyService : IService
{
    // ...
}

class MyStateStore : IStateStore
{
    // Inject IService on creation of the state store.
    public MyStateStore(IService service)
    {
        // ...
    }

    // ...
}

Logging

.NET Dapr pluggable components can use the standard .NET logging mechanisms. The DaprPluggableComponentsApplication exposes an ILoggingBuilder, through which it can be configured.

var app = DaprPluggableComponentsApplication.Create();

// Reset the default loggers and setup new ones.
app.Logging.ClearProviders();
app.Logging.AddConsole();

app.RegisterService(
    "<service name>",
    serviceBuilder =>
    {
        serviceBuilder.RegisterStateStore<MyStateStore>();
    });

app.Run();

class MyStateStore : IStateStore
{
    // Inject a logger on creation of the state store.
    public MyStateStore(ILogger<MyStateStore> logger)
    {
        // ...
    }

    // ...
}

Configuration Values

Since .NET pluggable components are built on ASP.NET, they can use its standard configuration mechanisms and default to the same set of pre-registered providers. The DaprPluggableComponentsApplication exposes an IConfigurationManager through which it can be configured.

var app = DaprPluggableComponentsApplication.Create();

// Reset the default configuration providers and add new ones.
((IConfigurationBuilder)app.Configuration).Sources.Clear();
app.Configuration.AddEnvironmentVariables();

// Get configuration value on startup.
const value = app.Configuration["<name>"];

app.RegisterService(
    "<service name>",
    serviceBuilder =>
    {
        serviceBuilder.RegisterStateStore<MyStateStore>();
    });

app.Run();

class MyStateStore : IStateStore
{
    // Inject the configuration on creation of the state store.
    public MyStateStore(IConfiguration configuration)
    {
        // ...
    }

    // ...
}

Next steps

2 - Lifetimes of .NET Dapr pluggable components

How to control the lifetime of a .NET pluggable component

There are two ways to register a component:

  • The component operates as a singleton, with lifetime managed by the SDK
  • A component’s lifetime is determined by the pluggable component and can be multi-instance or a singleton, as needed

Singleton components

Components registered by type are singletons: one instance will serve all configured components of that type associated with that socket. This approach is best when only a single component of that type exists and is shared amongst Dapr applications.

var app = DaprPluggableComponentsApplication.Create();

app.RegisterService(
    "service-a",
    serviceBuilder =>
    {
        serviceBuilder.RegisterStateStore<SingletonStateStore>();
    });

app.Run();

class SingletonStateStore : IStateStore
{
    // ...
}

Multi-instance components

Components can be registered by passing a “factory method”. This method will be called for each configured component of that type associated with that socket. The method returns the instance to associate with that component (whether shared or not). This approach is best when multiple components of the same type may be configured with different sets of metadata, when component operations need to be isolated from one another, etc.

The factory method will be passed context, such as the ID of the configured Dapr component, that can be used to differentiate component instances.

var app = DaprPluggableComponentsApplication.Create();

app.RegisterService(
    "service-a",
    serviceBuilder =>
    {
        serviceBuilder.RegisterStateStore(
            context =>
            {
                return new MultiStateStore(context.InstanceId);
            });
    });

app.Run();

class MultiStateStore : IStateStore
{
    private readonly string instanceId;

    public MultiStateStore(string instanceId)
    {
        this.instanceId = instanceId;
    }

    // ...
}

Next steps

3 - Multiple services in a .NET Dapr pluggable component

How to expose multiple services from a .NET pluggable component

A pluggable component can host multiple components of varying types. You might do this:

  • To minimize the number of sidecars running in a cluster
  • To group related components that are likely to share libraries and implementation, such as:
    • A database exposed both as a general state store, and
    • Output bindings that allow more specific operations.

Each Unix Domain Socket can manage calls to one component of each type. To host multiple components of the same type, you can spread those types across multiple sockets. The SDK binds each socket to a “service”, with each service composed of one or more component types.

Registering multiple services

Each call to RegisterService() binds a socket to a set of registered components, where one of each type of component can be registered per service.

var app = DaprPluggableComponentsApplication.Create();

app.RegisterService(
    "service-a",
    serviceBuilder =>
    {
        serviceBuilder.RegisterStateStore<MyDatabaseStateStore>();
        serviceBuilder.RegisterBinding<MyDatabaseOutputBinding>();
    });

app.RegisterService(
    "service-b",
    serviceBuilder =>
    {
        serviceBuilder.RegisterStateStore<AnotherStateStore>();
    });

app.Run();

class MyDatabaseStateStore : IStateStore
{
    // ...
}

class MyDatabaseOutputBinding : IOutputBinding
{
    // ...
}

class AnotherStateStore : IStateStore
{
    // ...
}

Configuring Multiple Components

Configuring Dapr to use the hosted components is the same as for any single component - the component YAML refers to the associated socket.

#
# This component uses the state store associated with socket `state-store-a`
#
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: state-store-a
spec:
  type: state.service-a
  version: v1
  metadata: []
#
# This component uses the state store associated with socket `state-store-b`
#
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: state-store-b
spec:
  type: state.service-b
  version: v1
  metadata: []

Next steps