/* Code generated by Azure.Iot.Operations.ProtocolCompilerLib v1.0.0.0; DO NOT EDIT. */

#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Azure.Iot.Operations.Protocol.Models;
using Azure.Iot.Operations.Protocol;
using Azure.Iot.Operations.Protocol.RPC;
using Azure.Iot.Operations.Protocol.Telemetry;
using Azure.Iot.Operations.Services.AssetAndDeviceRegistry.Generated.Common;

namespace Azure.Iot.Operations.Services.AssetAndDeviceRegistry.Generated.AdrBaseService
{
    [System.CodeDom.Compiler.GeneratedCode("Azure.Iot.Operations.ProtocolCompilerLib", "1.0.0.0")]
    public static partial class AdrBaseService
    {
        public abstract partial class Service : IAsyncDisposable
        {
            private ApplicationContext applicationContext;
            private IMqttPubSubClient mqttClient;
            private readonly GetDeviceCommandExecutor getDeviceCommandExecutor;
            private readonly GetDeviceStatusCommandExecutor getDeviceStatusCommandExecutor;
            private readonly GetAssetCommandExecutor getAssetCommandExecutor;
            private readonly GetAssetStatusCommandExecutor getAssetStatusCommandExecutor;
            private readonly UpdateDeviceStatusCommandExecutor updateDeviceStatusCommandExecutor;
            private readonly UpdateAssetStatusCommandExecutor updateAssetStatusCommandExecutor;
            private readonly SetNotificationPreferenceForDeviceUpdatesCommandExecutor setNotificationPreferenceForDeviceUpdatesCommandExecutor;
            private readonly SetNotificationPreferenceForAssetUpdatesCommandExecutor setNotificationPreferenceForAssetUpdatesCommandExecutor;
            private readonly CreateOrUpdateDiscoveredAssetCommandExecutor createOrUpdateDiscoveredAssetCommandExecutor;
            private readonly DeviceEndpointRuntimeHealthEventTelemetrySender deviceEndpointRuntimeHealthEventTelemetrySender;
            private readonly DatasetRuntimeHealthEventTelemetrySender datasetRuntimeHealthEventTelemetrySender;
            private readonly EventRuntimeHealthEventTelemetrySender eventRuntimeHealthEventTelemetrySender;
            private readonly ManagementActionRuntimeHealthEventTelemetrySender managementActionRuntimeHealthEventTelemetrySender;
            private readonly StreamRuntimeHealthEventTelemetrySender streamRuntimeHealthEventTelemetrySender;
            private readonly DeviceUpdateEventTelemetrySender deviceUpdateEventTelemetrySender;
            private readonly AssetUpdateEventTelemetrySender assetUpdateEventTelemetrySender;

            /// <summary>
            /// Construct a new instance of this service.
            /// </summary>
            /// <param name="applicationContext">The shared context for your application.</param>
            /// <param name="mqttClient">The MQTT client to use.</param>
            /// <param name="topicTokenMap">
            /// The topic token replacement map to use for all operations by default. Generally, this will include the token values
            /// for topic tokens that should be the same for the duration of this service's lifetime. Note that
            /// additional topic tokens can be specified per event message.
            /// </param>
            public Service(ApplicationContext applicationContext, IMqttPubSubClient mqttClient, Dictionary<string, string>? topicTokenMap = null)
            {
                this.applicationContext = applicationContext;
                this.mqttClient = mqttClient;

                string? clientId = this.mqttClient.ClientId;
                if (string.IsNullOrEmpty(clientId))
                {
                    throw new InvalidOperationException("No MQTT client Id configured. Must connect to MQTT broker before invoking command.");
                }

                this.getDeviceCommandExecutor = new GetDeviceCommandExecutor(applicationContext, mqttClient) { OnCommandReceived = GetDeviceInt };
                this.getDeviceStatusCommandExecutor = new GetDeviceStatusCommandExecutor(applicationContext, mqttClient) { OnCommandReceived = GetDeviceStatusInt };
                this.getAssetCommandExecutor = new GetAssetCommandExecutor(applicationContext, mqttClient) { OnCommandReceived = GetAssetInt };
                this.getAssetStatusCommandExecutor = new GetAssetStatusCommandExecutor(applicationContext, mqttClient) { OnCommandReceived = GetAssetStatusInt };
                this.updateDeviceStatusCommandExecutor = new UpdateDeviceStatusCommandExecutor(applicationContext, mqttClient) { OnCommandReceived = UpdateDeviceStatusInt };
                this.updateAssetStatusCommandExecutor = new UpdateAssetStatusCommandExecutor(applicationContext, mqttClient) { OnCommandReceived = UpdateAssetStatusInt };
                this.setNotificationPreferenceForDeviceUpdatesCommandExecutor = new SetNotificationPreferenceForDeviceUpdatesCommandExecutor(applicationContext, mqttClient) { OnCommandReceived = SetNotificationPreferenceForDeviceUpdatesInt };
                this.setNotificationPreferenceForAssetUpdatesCommandExecutor = new SetNotificationPreferenceForAssetUpdatesCommandExecutor(applicationContext, mqttClient) { OnCommandReceived = SetNotificationPreferenceForAssetUpdatesInt };
                this.createOrUpdateDiscoveredAssetCommandExecutor = new CreateOrUpdateDiscoveredAssetCommandExecutor(applicationContext, mqttClient) { OnCommandReceived = CreateOrUpdateDiscoveredAssetInt };
                this.deviceEndpointRuntimeHealthEventTelemetrySender = new DeviceEndpointRuntimeHealthEventTelemetrySender(applicationContext, mqttClient);
                this.datasetRuntimeHealthEventTelemetrySender = new DatasetRuntimeHealthEventTelemetrySender(applicationContext, mqttClient);
                this.eventRuntimeHealthEventTelemetrySender = new EventRuntimeHealthEventTelemetrySender(applicationContext, mqttClient);
                this.managementActionRuntimeHealthEventTelemetrySender = new ManagementActionRuntimeHealthEventTelemetrySender(applicationContext, mqttClient);
                this.streamRuntimeHealthEventTelemetrySender = new StreamRuntimeHealthEventTelemetrySender(applicationContext, mqttClient);
                this.deviceUpdateEventTelemetrySender = new DeviceUpdateEventTelemetrySender(applicationContext, mqttClient);
                this.assetUpdateEventTelemetrySender = new AssetUpdateEventTelemetrySender(applicationContext, mqttClient);

                if (topicTokenMap != null)
                {
                    foreach (string topicTokenKey in topicTokenMap.Keys)
                    {
                        this.getDeviceCommandExecutor.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.getDeviceStatusCommandExecutor.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.getAssetCommandExecutor.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.getAssetStatusCommandExecutor.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.updateDeviceStatusCommandExecutor.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.updateAssetStatusCommandExecutor.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.setNotificationPreferenceForDeviceUpdatesCommandExecutor.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.setNotificationPreferenceForAssetUpdatesCommandExecutor.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.createOrUpdateDiscoveredAssetCommandExecutor.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.deviceEndpointRuntimeHealthEventTelemetrySender.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.datasetRuntimeHealthEventTelemetrySender.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.eventRuntimeHealthEventTelemetrySender.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.managementActionRuntimeHealthEventTelemetrySender.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.streamRuntimeHealthEventTelemetrySender.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.deviceUpdateEventTelemetrySender.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.assetUpdateEventTelemetrySender.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                    }
                }

                this.getDeviceCommandExecutor.TopicTokenMap.TryAdd("executorId", clientId);
                this.getDeviceStatusCommandExecutor.TopicTokenMap.TryAdd("executorId", clientId);
                this.getAssetCommandExecutor.TopicTokenMap.TryAdd("executorId", clientId);
                this.getAssetStatusCommandExecutor.TopicTokenMap.TryAdd("executorId", clientId);
                this.updateDeviceStatusCommandExecutor.TopicTokenMap.TryAdd("executorId", clientId);
                this.updateAssetStatusCommandExecutor.TopicTokenMap.TryAdd("executorId", clientId);
                this.setNotificationPreferenceForDeviceUpdatesCommandExecutor.TopicTokenMap.TryAdd("executorId", clientId);
                this.setNotificationPreferenceForAssetUpdatesCommandExecutor.TopicTokenMap.TryAdd("executorId", clientId);
                this.createOrUpdateDiscoveredAssetCommandExecutor.TopicTokenMap.TryAdd("executorId", clientId);
            }

            public GetDeviceCommandExecutor GetDeviceCommandExecutor { get => this.getDeviceCommandExecutor; }

            public GetDeviceStatusCommandExecutor GetDeviceStatusCommandExecutor { get => this.getDeviceStatusCommandExecutor; }

            public GetAssetCommandExecutor GetAssetCommandExecutor { get => this.getAssetCommandExecutor; }

            public GetAssetStatusCommandExecutor GetAssetStatusCommandExecutor { get => this.getAssetStatusCommandExecutor; }

            public UpdateDeviceStatusCommandExecutor UpdateDeviceStatusCommandExecutor { get => this.updateDeviceStatusCommandExecutor; }

            public UpdateAssetStatusCommandExecutor UpdateAssetStatusCommandExecutor { get => this.updateAssetStatusCommandExecutor; }

            public SetNotificationPreferenceForDeviceUpdatesCommandExecutor SetNotificationPreferenceForDeviceUpdatesCommandExecutor { get => this.setNotificationPreferenceForDeviceUpdatesCommandExecutor; }

            public SetNotificationPreferenceForAssetUpdatesCommandExecutor SetNotificationPreferenceForAssetUpdatesCommandExecutor { get => this.setNotificationPreferenceForAssetUpdatesCommandExecutor; }

            public CreateOrUpdateDiscoveredAssetCommandExecutor CreateOrUpdateDiscoveredAssetCommandExecutor { get => this.createOrUpdateDiscoveredAssetCommandExecutor; }

            public DeviceEndpointRuntimeHealthEventTelemetrySender DeviceEndpointRuntimeHealthEventTelemetrySender { get => this.deviceEndpointRuntimeHealthEventTelemetrySender; }

            public DatasetRuntimeHealthEventTelemetrySender DatasetRuntimeHealthEventTelemetrySender { get => this.datasetRuntimeHealthEventTelemetrySender; }

            public EventRuntimeHealthEventTelemetrySender EventRuntimeHealthEventTelemetrySender { get => this.eventRuntimeHealthEventTelemetrySender; }

            public ManagementActionRuntimeHealthEventTelemetrySender ManagementActionRuntimeHealthEventTelemetrySender { get => this.managementActionRuntimeHealthEventTelemetrySender; }

            public StreamRuntimeHealthEventTelemetrySender StreamRuntimeHealthEventTelemetrySender { get => this.streamRuntimeHealthEventTelemetrySender; }

            public DeviceUpdateEventTelemetrySender DeviceUpdateEventTelemetrySender { get => this.deviceUpdateEventTelemetrySender; }

            public AssetUpdateEventTelemetrySender AssetUpdateEventTelemetrySender { get => this.assetUpdateEventTelemetrySender; }

            public abstract Task<ExtendedResponse<GetDeviceResponsePayload>> GetDeviceAsync(CommandRequestMetadata requestMetadata, CancellationToken cancellationToken);

            public abstract Task<ExtendedResponse<GetDeviceStatusResponsePayload>> GetDeviceStatusAsync(CommandRequestMetadata requestMetadata, CancellationToken cancellationToken);

            public abstract Task<ExtendedResponse<GetAssetResponsePayload>> GetAssetAsync(GetAssetRequestPayload request, CommandRequestMetadata requestMetadata, CancellationToken cancellationToken);

            public abstract Task<ExtendedResponse<GetAssetStatusResponsePayload>> GetAssetStatusAsync(GetAssetStatusRequestPayload request, CommandRequestMetadata requestMetadata, CancellationToken cancellationToken);

            public abstract Task<ExtendedResponse<UpdateDeviceStatusResponsePayload>> UpdateDeviceStatusAsync(UpdateDeviceStatusRequestPayload request, CommandRequestMetadata requestMetadata, CancellationToken cancellationToken);

            public abstract Task<ExtendedResponse<UpdateAssetStatusResponsePayload>> UpdateAssetStatusAsync(UpdateAssetStatusRequestPayload request, CommandRequestMetadata requestMetadata, CancellationToken cancellationToken);

            public abstract Task<ExtendedResponse<SetNotificationPreferenceForDeviceUpdatesResponsePayload>> SetNotificationPreferenceForDeviceUpdatesAsync(SetNotificationPreferenceForDeviceUpdatesRequestPayload request, CommandRequestMetadata requestMetadata, CancellationToken cancellationToken);

            public abstract Task<ExtendedResponse<SetNotificationPreferenceForAssetUpdatesResponsePayload>> SetNotificationPreferenceForAssetUpdatesAsync(SetNotificationPreferenceForAssetUpdatesRequestPayload request, CommandRequestMetadata requestMetadata, CancellationToken cancellationToken);

            public abstract Task<ExtendedResponse<CreateOrUpdateDiscoveredAssetResponsePayload>> CreateOrUpdateDiscoveredAssetAsync(CreateOrUpdateDiscoveredAssetRequestPayload request, CommandRequestMetadata requestMetadata, CancellationToken cancellationToken);

            /// <summary>
            /// Send telemetry.
            /// </summary>
            /// <param name="telemetry">The payload of the telemetry.</param>
            /// <param name="metadata">The metadata of the telemetry.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic token map provided in the constructor. If this map
            /// contains any keys that topic token map provided in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="qos">The quality of service to send the telemetry with.</param>
            /// <param name="telemetryTimeout">How long the telemetry message will be available on the broker for a receiver to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            public async Task SendTelemetryAsync(DeviceEndpointRuntimeHealthEventTelemetry telemetry, OutgoingTelemetryMetadata metadata, Dictionary<string, string>? additionalTopicTokenMap = null, MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce, TimeSpan? telemetryTimeout = null, CancellationToken cancellationToken = default)
            {
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }
                await this.deviceEndpointRuntimeHealthEventTelemetrySender.SendTelemetryAsync(telemetry, metadata, prefixedAdditionalTopicTokenMap, qos, telemetryTimeout, cancellationToken);
            }

            /// <summary>
            /// Send telemetry.
            /// </summary>
            /// <param name="telemetry">The payload of the telemetry.</param>
            /// <param name="metadata">The metadata of the telemetry.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic token map provided in the constructor. If this map
            /// contains any keys that topic token map provided in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="qos">The quality of service to send the telemetry with.</param>
            /// <param name="telemetryTimeout">How long the telemetry message will be available on the broker for a receiver to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            public async Task SendTelemetryAsync(DatasetRuntimeHealthEventTelemetry telemetry, OutgoingTelemetryMetadata metadata, Dictionary<string, string>? additionalTopicTokenMap = null, MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce, TimeSpan? telemetryTimeout = null, CancellationToken cancellationToken = default)
            {
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }
                await this.datasetRuntimeHealthEventTelemetrySender.SendTelemetryAsync(telemetry, metadata, prefixedAdditionalTopicTokenMap, qos, telemetryTimeout, cancellationToken);
            }

            /// <summary>
            /// Send telemetry.
            /// </summary>
            /// <param name="telemetry">The payload of the telemetry.</param>
            /// <param name="metadata">The metadata of the telemetry.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic token map provided in the constructor. If this map
            /// contains any keys that topic token map provided in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="qos">The quality of service to send the telemetry with.</param>
            /// <param name="telemetryTimeout">How long the telemetry message will be available on the broker for a receiver to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            public async Task SendTelemetryAsync(EventRuntimeHealthEventTelemetry telemetry, OutgoingTelemetryMetadata metadata, Dictionary<string, string>? additionalTopicTokenMap = null, MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce, TimeSpan? telemetryTimeout = null, CancellationToken cancellationToken = default)
            {
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }
                await this.eventRuntimeHealthEventTelemetrySender.SendTelemetryAsync(telemetry, metadata, prefixedAdditionalTopicTokenMap, qos, telemetryTimeout, cancellationToken);
            }

            /// <summary>
            /// Send telemetry.
            /// </summary>
            /// <param name="telemetry">The payload of the telemetry.</param>
            /// <param name="metadata">The metadata of the telemetry.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic token map provided in the constructor. If this map
            /// contains any keys that topic token map provided in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="qos">The quality of service to send the telemetry with.</param>
            /// <param name="telemetryTimeout">How long the telemetry message will be available on the broker for a receiver to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            public async Task SendTelemetryAsync(ManagementActionRuntimeHealthEventTelemetry telemetry, OutgoingTelemetryMetadata metadata, Dictionary<string, string>? additionalTopicTokenMap = null, MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce, TimeSpan? telemetryTimeout = null, CancellationToken cancellationToken = default)
            {
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }
                await this.managementActionRuntimeHealthEventTelemetrySender.SendTelemetryAsync(telemetry, metadata, prefixedAdditionalTopicTokenMap, qos, telemetryTimeout, cancellationToken);
            }

            /// <summary>
            /// Send telemetry.
            /// </summary>
            /// <param name="telemetry">The payload of the telemetry.</param>
            /// <param name="metadata">The metadata of the telemetry.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic token map provided in the constructor. If this map
            /// contains any keys that topic token map provided in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="qos">The quality of service to send the telemetry with.</param>
            /// <param name="telemetryTimeout">How long the telemetry message will be available on the broker for a receiver to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            public async Task SendTelemetryAsync(StreamRuntimeHealthEventTelemetry telemetry, OutgoingTelemetryMetadata metadata, Dictionary<string, string>? additionalTopicTokenMap = null, MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce, TimeSpan? telemetryTimeout = null, CancellationToken cancellationToken = default)
            {
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }
                await this.streamRuntimeHealthEventTelemetrySender.SendTelemetryAsync(telemetry, metadata, prefixedAdditionalTopicTokenMap, qos, telemetryTimeout, cancellationToken);
            }

            /// <summary>
            /// Send telemetry.
            /// </summary>
            /// <param name="telemetry">The payload of the telemetry.</param>
            /// <param name="metadata">The metadata of the telemetry.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic token map provided in the constructor. If this map
            /// contains any keys that topic token map provided in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="qos">The quality of service to send the telemetry with.</param>
            /// <param name="telemetryTimeout">How long the telemetry message will be available on the broker for a receiver to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            public async Task SendTelemetryAsync(DeviceUpdateEventTelemetry telemetry, OutgoingTelemetryMetadata metadata, Dictionary<string, string>? additionalTopicTokenMap = null, MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce, TimeSpan? telemetryTimeout = null, CancellationToken cancellationToken = default)
            {
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }
                await this.deviceUpdateEventTelemetrySender.SendTelemetryAsync(telemetry, metadata, prefixedAdditionalTopicTokenMap, qos, telemetryTimeout, cancellationToken);
            }

            /// <summary>
            /// Send telemetry.
            /// </summary>
            /// <param name="telemetry">The payload of the telemetry.</param>
            /// <param name="metadata">The metadata of the telemetry.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic token map provided in the constructor. If this map
            /// contains any keys that topic token map provided in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="qos">The quality of service to send the telemetry with.</param>
            /// <param name="telemetryTimeout">How long the telemetry message will be available on the broker for a receiver to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            public async Task SendTelemetryAsync(AssetUpdateEventTelemetry telemetry, OutgoingTelemetryMetadata metadata, Dictionary<string, string>? additionalTopicTokenMap = null, MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce, TimeSpan? telemetryTimeout = null, CancellationToken cancellationToken = default)
            {
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }
                await this.assetUpdateEventTelemetrySender.SendTelemetryAsync(telemetry, metadata, prefixedAdditionalTopicTokenMap, qos, telemetryTimeout, cancellationToken);
            }

            /// <summary>
            /// Begin accepting command invocations for all command executors.
            /// </summary>
            /// <param name="preferredDispatchConcurrency">The dispatch concurrency count for the command response cache to use.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            public async Task StartAsync(int? preferredDispatchConcurrency = null, CancellationToken cancellationToken = default)
            {
                string? clientId = this.mqttClient.ClientId;
                if (string.IsNullOrEmpty(clientId))
                {
                    throw new InvalidOperationException("No MQTT client Id configured. Must connect to MQTT broker before starting service.");
                }

                await Task.WhenAll(
                    this.getDeviceCommandExecutor.StartAsync(preferredDispatchConcurrency, cancellationToken),
                    this.getDeviceStatusCommandExecutor.StartAsync(preferredDispatchConcurrency, cancellationToken),
                    this.getAssetCommandExecutor.StartAsync(preferredDispatchConcurrency, cancellationToken),
                    this.getAssetStatusCommandExecutor.StartAsync(preferredDispatchConcurrency, cancellationToken),
                    this.updateDeviceStatusCommandExecutor.StartAsync(preferredDispatchConcurrency, cancellationToken),
                    this.updateAssetStatusCommandExecutor.StartAsync(preferredDispatchConcurrency, cancellationToken),
                    this.setNotificationPreferenceForDeviceUpdatesCommandExecutor.StartAsync(preferredDispatchConcurrency, cancellationToken),
                    this.setNotificationPreferenceForAssetUpdatesCommandExecutor.StartAsync(preferredDispatchConcurrency, cancellationToken),
                    this.createOrUpdateDiscoveredAssetCommandExecutor.StartAsync(preferredDispatchConcurrency, cancellationToken)).ConfigureAwait(false);
            }

            public async Task StopAsync(CancellationToken cancellationToken = default)
            {
                await Task.WhenAll(
                    this.getDeviceCommandExecutor.StopAsync(cancellationToken),
                    this.getDeviceStatusCommandExecutor.StopAsync(cancellationToken),
                    this.getAssetCommandExecutor.StopAsync(cancellationToken),
                    this.getAssetStatusCommandExecutor.StopAsync(cancellationToken),
                    this.updateDeviceStatusCommandExecutor.StopAsync(cancellationToken),
                    this.updateAssetStatusCommandExecutor.StopAsync(cancellationToken),
                    this.setNotificationPreferenceForDeviceUpdatesCommandExecutor.StopAsync(cancellationToken),
                    this.setNotificationPreferenceForAssetUpdatesCommandExecutor.StopAsync(cancellationToken),
                    this.createOrUpdateDiscoveredAssetCommandExecutor.StopAsync(cancellationToken)).ConfigureAwait(false);
            }

            private async Task<ExtendedResponse<GetDeviceResponseSchema>> GetDeviceInt(ExtendedRequest<EmptyJson> req, CancellationToken cancellationToken)
            {
                try
                {
                    ExtendedResponse<GetDeviceResponsePayload> extended = await this.GetDeviceAsync(req.RequestMetadata!, cancellationToken);

                    return new ExtendedResponse<GetDeviceResponseSchema>
                    {
                        Response = new GetDeviceResponseSchema
                        {
                            Device = extended.Response.Device,
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
                catch (AkriServiceErrorException intEx)
                {
                    ExtendedResponse<GetDeviceResponseSchema> extendedResponse = ExtendedResponse<GetDeviceResponseSchema>.CreateFromResponse(new GetDeviceResponseSchema { GetDeviceError = intEx.AkriServiceError });
                    return extendedResponse;
                }
            }

            private async Task<ExtendedResponse<GetDeviceStatusResponseSchema>> GetDeviceStatusInt(ExtendedRequest<EmptyJson> req, CancellationToken cancellationToken)
            {
                try
                {
                    ExtendedResponse<GetDeviceStatusResponsePayload> extended = await this.GetDeviceStatusAsync(req.RequestMetadata!, cancellationToken);

                    return new ExtendedResponse<GetDeviceStatusResponseSchema>
                    {
                        Response = new GetDeviceStatusResponseSchema
                        {
                            DeviceStatus = extended.Response.DeviceStatus,
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
                catch (AkriServiceErrorException intEx)
                {
                    ExtendedResponse<GetDeviceStatusResponseSchema> extendedResponse = ExtendedResponse<GetDeviceStatusResponseSchema>.CreateFromResponse(new GetDeviceStatusResponseSchema { GetDeviceStatusError = intEx.AkriServiceError });
                    return extendedResponse;
                }
            }

            private async Task<ExtendedResponse<GetAssetResponseSchema>> GetAssetInt(ExtendedRequest<GetAssetRequestPayload> req, CancellationToken cancellationToken)
            {
                try
                {
                    ExtendedResponse<GetAssetResponsePayload> extended = await this.GetAssetAsync(req.Request!, req.RequestMetadata!, cancellationToken);

                    return new ExtendedResponse<GetAssetResponseSchema>
                    {
                        Response = new GetAssetResponseSchema
                        {
                            Asset = extended.Response.Asset,
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
                catch (AkriServiceErrorException intEx)
                {
                    ExtendedResponse<GetAssetResponseSchema> extendedResponse = ExtendedResponse<GetAssetResponseSchema>.CreateFromResponse(new GetAssetResponseSchema { GetAssetError = intEx.AkriServiceError });
                    return extendedResponse;
                }
            }

            private async Task<ExtendedResponse<GetAssetStatusResponseSchema>> GetAssetStatusInt(ExtendedRequest<GetAssetStatusRequestPayload> req, CancellationToken cancellationToken)
            {
                try
                {
                    ExtendedResponse<GetAssetStatusResponsePayload> extended = await this.GetAssetStatusAsync(req.Request!, req.RequestMetadata!, cancellationToken);

                    return new ExtendedResponse<GetAssetStatusResponseSchema>
                    {
                        Response = new GetAssetStatusResponseSchema
                        {
                            AssetStatus = extended.Response.AssetStatus,
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
                catch (AkriServiceErrorException intEx)
                {
                    ExtendedResponse<GetAssetStatusResponseSchema> extendedResponse = ExtendedResponse<GetAssetStatusResponseSchema>.CreateFromResponse(new GetAssetStatusResponseSchema { GetAssetStatusError = intEx.AkriServiceError });
                    return extendedResponse;
                }
            }

            private async Task<ExtendedResponse<UpdateDeviceStatusResponseSchema>> UpdateDeviceStatusInt(ExtendedRequest<UpdateDeviceStatusRequestPayload> req, CancellationToken cancellationToken)
            {
                try
                {
                    ExtendedResponse<UpdateDeviceStatusResponsePayload> extended = await this.UpdateDeviceStatusAsync(req.Request!, req.RequestMetadata!, cancellationToken);

                    return new ExtendedResponse<UpdateDeviceStatusResponseSchema>
                    {
                        Response = new UpdateDeviceStatusResponseSchema
                        {
                            UpdatedDeviceStatus = extended.Response.UpdatedDeviceStatus,
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
                catch (AkriServiceErrorException intEx)
                {
                    ExtendedResponse<UpdateDeviceStatusResponseSchema> extendedResponse = ExtendedResponse<UpdateDeviceStatusResponseSchema>.CreateFromResponse(new UpdateDeviceStatusResponseSchema { UpdateDeviceStatusError = intEx.AkriServiceError });
                    return extendedResponse;
                }
            }

            private async Task<ExtendedResponse<UpdateAssetStatusResponseSchema>> UpdateAssetStatusInt(ExtendedRequest<UpdateAssetStatusRequestPayload> req, CancellationToken cancellationToken)
            {
                try
                {
                    ExtendedResponse<UpdateAssetStatusResponsePayload> extended = await this.UpdateAssetStatusAsync(req.Request!, req.RequestMetadata!, cancellationToken);

                    return new ExtendedResponse<UpdateAssetStatusResponseSchema>
                    {
                        Response = new UpdateAssetStatusResponseSchema
                        {
                            UpdatedAssetStatus = extended.Response.UpdatedAssetStatus,
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
                catch (AkriServiceErrorException intEx)
                {
                    ExtendedResponse<UpdateAssetStatusResponseSchema> extendedResponse = ExtendedResponse<UpdateAssetStatusResponseSchema>.CreateFromResponse(new UpdateAssetStatusResponseSchema { UpdateAssetStatusError = intEx.AkriServiceError });
                    return extendedResponse;
                }
            }

            private async Task<ExtendedResponse<SetNotificationPreferenceForDeviceUpdatesResponseSchema>> SetNotificationPreferenceForDeviceUpdatesInt(ExtendedRequest<SetNotificationPreferenceForDeviceUpdatesRequestPayload> req, CancellationToken cancellationToken)
            {
                try
                {
                    ExtendedResponse<SetNotificationPreferenceForDeviceUpdatesResponsePayload> extended = await this.SetNotificationPreferenceForDeviceUpdatesAsync(req.Request!, req.RequestMetadata!, cancellationToken);

                    return new ExtendedResponse<SetNotificationPreferenceForDeviceUpdatesResponseSchema>
                    {
                        Response = new SetNotificationPreferenceForDeviceUpdatesResponseSchema
                        {
                            ResponsePayload = extended.Response.ResponsePayload,
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
                catch (AkriServiceErrorException intEx)
                {
                    ExtendedResponse<SetNotificationPreferenceForDeviceUpdatesResponseSchema> extendedResponse = ExtendedResponse<SetNotificationPreferenceForDeviceUpdatesResponseSchema>.CreateFromResponse(new SetNotificationPreferenceForDeviceUpdatesResponseSchema { SetNotificationPreferenceForDeviceUpdatesError = intEx.AkriServiceError });
                    return extendedResponse;
                }
            }

            private async Task<ExtendedResponse<SetNotificationPreferenceForAssetUpdatesResponseSchema>> SetNotificationPreferenceForAssetUpdatesInt(ExtendedRequest<SetNotificationPreferenceForAssetUpdatesRequestPayload> req, CancellationToken cancellationToken)
            {
                try
                {
                    ExtendedResponse<SetNotificationPreferenceForAssetUpdatesResponsePayload> extended = await this.SetNotificationPreferenceForAssetUpdatesAsync(req.Request!, req.RequestMetadata!, cancellationToken);

                    return new ExtendedResponse<SetNotificationPreferenceForAssetUpdatesResponseSchema>
                    {
                        Response = new SetNotificationPreferenceForAssetUpdatesResponseSchema
                        {
                            ResponsePayload = extended.Response.ResponsePayload,
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
                catch (AkriServiceErrorException intEx)
                {
                    ExtendedResponse<SetNotificationPreferenceForAssetUpdatesResponseSchema> extendedResponse = ExtendedResponse<SetNotificationPreferenceForAssetUpdatesResponseSchema>.CreateFromResponse(new SetNotificationPreferenceForAssetUpdatesResponseSchema { SetNotificationPreferenceForAssetUpdatesError = intEx.AkriServiceError });
                    return extendedResponse;
                }
            }

            private async Task<ExtendedResponse<CreateOrUpdateDiscoveredAssetResponseSchema>> CreateOrUpdateDiscoveredAssetInt(ExtendedRequest<CreateOrUpdateDiscoveredAssetRequestPayload> req, CancellationToken cancellationToken)
            {
                try
                {
                    ExtendedResponse<CreateOrUpdateDiscoveredAssetResponsePayload> extended = await this.CreateOrUpdateDiscoveredAssetAsync(req.Request!, req.RequestMetadata!, cancellationToken);

                    return new ExtendedResponse<CreateOrUpdateDiscoveredAssetResponseSchema>
                    {
                        Response = new CreateOrUpdateDiscoveredAssetResponseSchema
                        {
                            DiscoveredAssetResponse = extended.Response.DiscoveredAssetResponse,
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
                catch (AkriServiceErrorException intEx)
                {
                    ExtendedResponse<CreateOrUpdateDiscoveredAssetResponseSchema> extendedResponse = ExtendedResponse<CreateOrUpdateDiscoveredAssetResponseSchema>.CreateFromResponse(new CreateOrUpdateDiscoveredAssetResponseSchema { CreateOrUpdateDiscoveredAssetError = intEx.AkriServiceError });
                    return extendedResponse;
                }
            }

            public async ValueTask DisposeAsync()
            {
                await this.getDeviceCommandExecutor.DisposeAsync().ConfigureAwait(false);
                await this.getDeviceStatusCommandExecutor.DisposeAsync().ConfigureAwait(false);
                await this.getAssetCommandExecutor.DisposeAsync().ConfigureAwait(false);
                await this.getAssetStatusCommandExecutor.DisposeAsync().ConfigureAwait(false);
                await this.updateDeviceStatusCommandExecutor.DisposeAsync().ConfigureAwait(false);
                await this.updateAssetStatusCommandExecutor.DisposeAsync().ConfigureAwait(false);
                await this.setNotificationPreferenceForDeviceUpdatesCommandExecutor.DisposeAsync().ConfigureAwait(false);
                await this.setNotificationPreferenceForAssetUpdatesCommandExecutor.DisposeAsync().ConfigureAwait(false);
                await this.createOrUpdateDiscoveredAssetCommandExecutor.DisposeAsync().ConfigureAwait(false);
                await this.deviceEndpointRuntimeHealthEventTelemetrySender.DisposeAsync().ConfigureAwait(false);
                await this.datasetRuntimeHealthEventTelemetrySender.DisposeAsync().ConfigureAwait(false);
                await this.eventRuntimeHealthEventTelemetrySender.DisposeAsync().ConfigureAwait(false);
                await this.managementActionRuntimeHealthEventTelemetrySender.DisposeAsync().ConfigureAwait(false);
                await this.streamRuntimeHealthEventTelemetrySender.DisposeAsync().ConfigureAwait(false);
                await this.deviceUpdateEventTelemetrySender.DisposeAsync().ConfigureAwait(false);
                await this.assetUpdateEventTelemetrySender.DisposeAsync().ConfigureAwait(false);
            }

            public async ValueTask DisposeAsync(bool disposing, CancellationToken cancellationToken = default)
            {
                await this.getDeviceCommandExecutor.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.getDeviceStatusCommandExecutor.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.getAssetCommandExecutor.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.getAssetStatusCommandExecutor.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.updateDeviceStatusCommandExecutor.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.updateAssetStatusCommandExecutor.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.setNotificationPreferenceForDeviceUpdatesCommandExecutor.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.setNotificationPreferenceForAssetUpdatesCommandExecutor.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.createOrUpdateDiscoveredAssetCommandExecutor.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.deviceEndpointRuntimeHealthEventTelemetrySender.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.datasetRuntimeHealthEventTelemetrySender.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.eventRuntimeHealthEventTelemetrySender.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.managementActionRuntimeHealthEventTelemetrySender.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.streamRuntimeHealthEventTelemetrySender.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.deviceUpdateEventTelemetrySender.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.assetUpdateEventTelemetrySender.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
            }
        }

        public abstract partial class Client : IAsyncDisposable
        {
            private ApplicationContext applicationContext;
            private IMqttPubSubClient mqttClient;
            private readonly GetDeviceCommandInvoker getDeviceCommandInvoker;
            private readonly GetDeviceStatusCommandInvoker getDeviceStatusCommandInvoker;
            private readonly GetAssetCommandInvoker getAssetCommandInvoker;
            private readonly GetAssetStatusCommandInvoker getAssetStatusCommandInvoker;
            private readonly UpdateDeviceStatusCommandInvoker updateDeviceStatusCommandInvoker;
            private readonly UpdateAssetStatusCommandInvoker updateAssetStatusCommandInvoker;
            private readonly SetNotificationPreferenceForDeviceUpdatesCommandInvoker setNotificationPreferenceForDeviceUpdatesCommandInvoker;
            private readonly SetNotificationPreferenceForAssetUpdatesCommandInvoker setNotificationPreferenceForAssetUpdatesCommandInvoker;
            private readonly CreateOrUpdateDiscoveredAssetCommandInvoker createOrUpdateDiscoveredAssetCommandInvoker;
            private readonly DeviceEndpointRuntimeHealthEventTelemetryReceiver deviceEndpointRuntimeHealthEventTelemetryReceiver;
            private readonly DatasetRuntimeHealthEventTelemetryReceiver datasetRuntimeHealthEventTelemetryReceiver;
            private readonly EventRuntimeHealthEventTelemetryReceiver eventRuntimeHealthEventTelemetryReceiver;
            private readonly ManagementActionRuntimeHealthEventTelemetryReceiver managementActionRuntimeHealthEventTelemetryReceiver;
            private readonly StreamRuntimeHealthEventTelemetryReceiver streamRuntimeHealthEventTelemetryReceiver;
            private readonly DeviceUpdateEventTelemetryReceiver deviceUpdateEventTelemetryReceiver;
            private readonly AssetUpdateEventTelemetryReceiver assetUpdateEventTelemetryReceiver;

            /// <summary>
            /// Construct a new instance of this client.
            /// </summary>
            /// <param name="applicationContext">The shared context for your application.</param>
            /// <param name="mqttClient">The MQTT client to use.</param>
            /// <param name="topicTokenMap">
            /// The topic token replacement map to use for all operations by default. Generally, this will include the token values
            /// for topic tokens that should be the same for the duration of this client's lifetime.
            /// </param>
            public Client(ApplicationContext applicationContext, IMqttPubSubClient mqttClient, Dictionary<string, string>? topicTokenMap = null)
            {
                this.applicationContext = applicationContext;
                this.mqttClient = mqttClient;

                this.getDeviceCommandInvoker = new GetDeviceCommandInvoker(applicationContext, mqttClient);
                this.getDeviceStatusCommandInvoker = new GetDeviceStatusCommandInvoker(applicationContext, mqttClient);
                this.getAssetCommandInvoker = new GetAssetCommandInvoker(applicationContext, mqttClient);
                this.getAssetStatusCommandInvoker = new GetAssetStatusCommandInvoker(applicationContext, mqttClient);
                this.updateDeviceStatusCommandInvoker = new UpdateDeviceStatusCommandInvoker(applicationContext, mqttClient);
                this.updateAssetStatusCommandInvoker = new UpdateAssetStatusCommandInvoker(applicationContext, mqttClient);
                this.setNotificationPreferenceForDeviceUpdatesCommandInvoker = new SetNotificationPreferenceForDeviceUpdatesCommandInvoker(applicationContext, mqttClient);
                this.setNotificationPreferenceForAssetUpdatesCommandInvoker = new SetNotificationPreferenceForAssetUpdatesCommandInvoker(applicationContext, mqttClient);
                this.createOrUpdateDiscoveredAssetCommandInvoker = new CreateOrUpdateDiscoveredAssetCommandInvoker(applicationContext, mqttClient);
                this.deviceEndpointRuntimeHealthEventTelemetryReceiver = new DeviceEndpointRuntimeHealthEventTelemetryReceiver(applicationContext, mqttClient) { OnTelemetryReceived = this.ReceiveTelemetry };
                this.datasetRuntimeHealthEventTelemetryReceiver = new DatasetRuntimeHealthEventTelemetryReceiver(applicationContext, mqttClient) { OnTelemetryReceived = this.ReceiveTelemetry };
                this.eventRuntimeHealthEventTelemetryReceiver = new EventRuntimeHealthEventTelemetryReceiver(applicationContext, mqttClient) { OnTelemetryReceived = this.ReceiveTelemetry };
                this.managementActionRuntimeHealthEventTelemetryReceiver = new ManagementActionRuntimeHealthEventTelemetryReceiver(applicationContext, mqttClient) { OnTelemetryReceived = this.ReceiveTelemetry };
                this.streamRuntimeHealthEventTelemetryReceiver = new StreamRuntimeHealthEventTelemetryReceiver(applicationContext, mqttClient) { OnTelemetryReceived = this.ReceiveTelemetry };
                this.deviceUpdateEventTelemetryReceiver = new DeviceUpdateEventTelemetryReceiver(applicationContext, mqttClient) { OnTelemetryReceived = this.ReceiveTelemetry };
                this.assetUpdateEventTelemetryReceiver = new AssetUpdateEventTelemetryReceiver(applicationContext, mqttClient) { OnTelemetryReceived = this.ReceiveTelemetry };

                if (topicTokenMap != null)
                {
                    foreach (string topicTokenKey in topicTokenMap.Keys)
                    {
                        this.getDeviceCommandInvoker.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.getDeviceStatusCommandInvoker.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.getAssetCommandInvoker.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.getAssetStatusCommandInvoker.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.updateDeviceStatusCommandInvoker.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.updateAssetStatusCommandInvoker.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.setNotificationPreferenceForDeviceUpdatesCommandInvoker.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.setNotificationPreferenceForAssetUpdatesCommandInvoker.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.createOrUpdateDiscoveredAssetCommandInvoker.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.deviceEndpointRuntimeHealthEventTelemetryReceiver.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.datasetRuntimeHealthEventTelemetryReceiver.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.eventRuntimeHealthEventTelemetryReceiver.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.managementActionRuntimeHealthEventTelemetryReceiver.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.streamRuntimeHealthEventTelemetryReceiver.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.deviceUpdateEventTelemetryReceiver.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                        this.assetUpdateEventTelemetryReceiver.TopicTokenMap.TryAdd("ex:" + topicTokenKey, topicTokenMap[topicTokenKey]);
                    }
                }
            }

            public GetDeviceCommandInvoker GetDeviceCommandInvoker { get => this.getDeviceCommandInvoker; }

            public GetDeviceStatusCommandInvoker GetDeviceStatusCommandInvoker { get => this.getDeviceStatusCommandInvoker; }

            public GetAssetCommandInvoker GetAssetCommandInvoker { get => this.getAssetCommandInvoker; }

            public GetAssetStatusCommandInvoker GetAssetStatusCommandInvoker { get => this.getAssetStatusCommandInvoker; }

            public UpdateDeviceStatusCommandInvoker UpdateDeviceStatusCommandInvoker { get => this.updateDeviceStatusCommandInvoker; }

            public UpdateAssetStatusCommandInvoker UpdateAssetStatusCommandInvoker { get => this.updateAssetStatusCommandInvoker; }

            public SetNotificationPreferenceForDeviceUpdatesCommandInvoker SetNotificationPreferenceForDeviceUpdatesCommandInvoker { get => this.setNotificationPreferenceForDeviceUpdatesCommandInvoker; }

            public SetNotificationPreferenceForAssetUpdatesCommandInvoker SetNotificationPreferenceForAssetUpdatesCommandInvoker { get => this.setNotificationPreferenceForAssetUpdatesCommandInvoker; }

            public CreateOrUpdateDiscoveredAssetCommandInvoker CreateOrUpdateDiscoveredAssetCommandInvoker { get => this.createOrUpdateDiscoveredAssetCommandInvoker; }

            public DeviceEndpointRuntimeHealthEventTelemetryReceiver DeviceEndpointRuntimeHealthEventTelemetryReceiver { get => this.deviceEndpointRuntimeHealthEventTelemetryReceiver; }

            public DatasetRuntimeHealthEventTelemetryReceiver DatasetRuntimeHealthEventTelemetryReceiver { get => this.datasetRuntimeHealthEventTelemetryReceiver; }

            public EventRuntimeHealthEventTelemetryReceiver EventRuntimeHealthEventTelemetryReceiver { get => this.eventRuntimeHealthEventTelemetryReceiver; }

            public ManagementActionRuntimeHealthEventTelemetryReceiver ManagementActionRuntimeHealthEventTelemetryReceiver { get => this.managementActionRuntimeHealthEventTelemetryReceiver; }

            public StreamRuntimeHealthEventTelemetryReceiver StreamRuntimeHealthEventTelemetryReceiver { get => this.streamRuntimeHealthEventTelemetryReceiver; }

            public DeviceUpdateEventTelemetryReceiver DeviceUpdateEventTelemetryReceiver { get => this.deviceUpdateEventTelemetryReceiver; }

            public AssetUpdateEventTelemetryReceiver AssetUpdateEventTelemetryReceiver { get => this.assetUpdateEventTelemetryReceiver; }

            public abstract Task ReceiveTelemetry(string senderId, DeviceEndpointRuntimeHealthEventTelemetry telemetry, IncomingTelemetryMetadata metadata);

            public abstract Task ReceiveTelemetry(string senderId, DatasetRuntimeHealthEventTelemetry telemetry, IncomingTelemetryMetadata metadata);

            public abstract Task ReceiveTelemetry(string senderId, EventRuntimeHealthEventTelemetry telemetry, IncomingTelemetryMetadata metadata);

            public abstract Task ReceiveTelemetry(string senderId, ManagementActionRuntimeHealthEventTelemetry telemetry, IncomingTelemetryMetadata metadata);

            public abstract Task ReceiveTelemetry(string senderId, StreamRuntimeHealthEventTelemetry telemetry, IncomingTelemetryMetadata metadata);

            public abstract Task ReceiveTelemetry(string senderId, DeviceUpdateEventTelemetry telemetry, IncomingTelemetryMetadata metadata);

            public abstract Task ReceiveTelemetry(string senderId, AssetUpdateEventTelemetry telemetry, IncomingTelemetryMetadata metadata);

            /// <summary>
            /// Invoke a command.
            /// </summary>
            /// <param name="requestMetadata">The metadata for this command request.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic tokens specified in the constructor. If this map
            /// contains any keys that the topic tokens specified in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="commandTimeout">How long the command will be available on the broker for an executor to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            /// <returns>The command response.</returns>
            public RpcCallAsync<GetDeviceResponsePayload> GetDeviceAsync(CommandRequestMetadata? requestMetadata = null, Dictionary<string, string>? additionalTopicTokenMap = null, TimeSpan? commandTimeout = default, CancellationToken cancellationToken = default)
            {
                string? clientId = this.mqttClient.ClientId;
                if (string.IsNullOrEmpty(clientId))
                {
                    throw new InvalidOperationException("No MQTT client Id configured. Must connect to MQTT broker before invoking command.");
                }

                CommandRequestMetadata metadata = requestMetadata ?? new CommandRequestMetadata();
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }

                prefixedAdditionalTopicTokenMap["invokerClientId"] = clientId;

                return new RpcCallAsync<GetDeviceResponsePayload>(this.GetDeviceInt(new EmptyJson(), metadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken), metadata.CorrelationId);
            }

            /// <summary>
            /// Invoke a command.
            /// </summary>
            /// <param name="requestMetadata">The metadata for this command request.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic tokens specified in the constructor. If this map
            /// contains any keys that the topic tokens specified in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="commandTimeout">How long the command will be available on the broker for an executor to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            /// <returns>The command response.</returns>
            public RpcCallAsync<GetDeviceStatusResponsePayload> GetDeviceStatusAsync(CommandRequestMetadata? requestMetadata = null, Dictionary<string, string>? additionalTopicTokenMap = null, TimeSpan? commandTimeout = default, CancellationToken cancellationToken = default)
            {
                string? clientId = this.mqttClient.ClientId;
                if (string.IsNullOrEmpty(clientId))
                {
                    throw new InvalidOperationException("No MQTT client Id configured. Must connect to MQTT broker before invoking command.");
                }

                CommandRequestMetadata metadata = requestMetadata ?? new CommandRequestMetadata();
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }

                prefixedAdditionalTopicTokenMap["invokerClientId"] = clientId;

                return new RpcCallAsync<GetDeviceStatusResponsePayload>(this.GetDeviceStatusInt(new EmptyJson(), metadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken), metadata.CorrelationId);
            }

            /// <summary>
            /// Invoke a command.
            /// </summary>
            /// <param name="request">The data for this command request.</param>
            /// <param name="requestMetadata">The metadata for this command request.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic tokens specified in the constructor. If this map
            /// contains any keys that the topic tokens specified in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="commandTimeout">How long the command will be available on the broker for an executor to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            /// <returns>The command response.</returns>
            public RpcCallAsync<GetAssetResponsePayload> GetAssetAsync(GetAssetRequestPayload request, CommandRequestMetadata? requestMetadata = null, Dictionary<string, string>? additionalTopicTokenMap = null, TimeSpan? commandTimeout = default, CancellationToken cancellationToken = default)
            {
                string? clientId = this.mqttClient.ClientId;
                if (string.IsNullOrEmpty(clientId))
                {
                    throw new InvalidOperationException("No MQTT client Id configured. Must connect to MQTT broker before invoking command.");
                }

                CommandRequestMetadata metadata = requestMetadata ?? new CommandRequestMetadata();
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }

                prefixedAdditionalTopicTokenMap["invokerClientId"] = clientId;

                return new RpcCallAsync<GetAssetResponsePayload>(this.GetAssetInt(request, metadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken), metadata.CorrelationId);
            }

            /// <summary>
            /// Invoke a command.
            /// </summary>
            /// <param name="request">The data for this command request.</param>
            /// <param name="requestMetadata">The metadata for this command request.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic tokens specified in the constructor. If this map
            /// contains any keys that the topic tokens specified in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="commandTimeout">How long the command will be available on the broker for an executor to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            /// <returns>The command response.</returns>
            public RpcCallAsync<GetAssetStatusResponsePayload> GetAssetStatusAsync(GetAssetStatusRequestPayload request, CommandRequestMetadata? requestMetadata = null, Dictionary<string, string>? additionalTopicTokenMap = null, TimeSpan? commandTimeout = default, CancellationToken cancellationToken = default)
            {
                string? clientId = this.mqttClient.ClientId;
                if (string.IsNullOrEmpty(clientId))
                {
                    throw new InvalidOperationException("No MQTT client Id configured. Must connect to MQTT broker before invoking command.");
                }

                CommandRequestMetadata metadata = requestMetadata ?? new CommandRequestMetadata();
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }

                prefixedAdditionalTopicTokenMap["invokerClientId"] = clientId;

                return new RpcCallAsync<GetAssetStatusResponsePayload>(this.GetAssetStatusInt(request, metadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken), metadata.CorrelationId);
            }

            /// <summary>
            /// Invoke a command.
            /// </summary>
            /// <param name="request">The data for this command request.</param>
            /// <param name="requestMetadata">The metadata for this command request.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic tokens specified in the constructor. If this map
            /// contains any keys that the topic tokens specified in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="commandTimeout">How long the command will be available on the broker for an executor to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            /// <returns>The command response.</returns>
            public RpcCallAsync<UpdateDeviceStatusResponsePayload> UpdateDeviceStatusAsync(UpdateDeviceStatusRequestPayload request, CommandRequestMetadata? requestMetadata = null, Dictionary<string, string>? additionalTopicTokenMap = null, TimeSpan? commandTimeout = default, CancellationToken cancellationToken = default)
            {
                string? clientId = this.mqttClient.ClientId;
                if (string.IsNullOrEmpty(clientId))
                {
                    throw new InvalidOperationException("No MQTT client Id configured. Must connect to MQTT broker before invoking command.");
                }

                CommandRequestMetadata metadata = requestMetadata ?? new CommandRequestMetadata();
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }

                prefixedAdditionalTopicTokenMap["invokerClientId"] = clientId;

                return new RpcCallAsync<UpdateDeviceStatusResponsePayload>(this.UpdateDeviceStatusInt(request, metadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken), metadata.CorrelationId);
            }

            /// <summary>
            /// Invoke a command.
            /// </summary>
            /// <param name="request">The data for this command request.</param>
            /// <param name="requestMetadata">The metadata for this command request.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic tokens specified in the constructor. If this map
            /// contains any keys that the topic tokens specified in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="commandTimeout">How long the command will be available on the broker for an executor to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            /// <returns>The command response.</returns>
            public RpcCallAsync<UpdateAssetStatusResponsePayload> UpdateAssetStatusAsync(UpdateAssetStatusRequestPayload request, CommandRequestMetadata? requestMetadata = null, Dictionary<string, string>? additionalTopicTokenMap = null, TimeSpan? commandTimeout = default, CancellationToken cancellationToken = default)
            {
                string? clientId = this.mqttClient.ClientId;
                if (string.IsNullOrEmpty(clientId))
                {
                    throw new InvalidOperationException("No MQTT client Id configured. Must connect to MQTT broker before invoking command.");
                }

                CommandRequestMetadata metadata = requestMetadata ?? new CommandRequestMetadata();
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }

                prefixedAdditionalTopicTokenMap["invokerClientId"] = clientId;

                return new RpcCallAsync<UpdateAssetStatusResponsePayload>(this.UpdateAssetStatusInt(request, metadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken), metadata.CorrelationId);
            }

            /// <summary>
            /// Invoke a command.
            /// </summary>
            /// <param name="request">The data for this command request.</param>
            /// <param name="requestMetadata">The metadata for this command request.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic tokens specified in the constructor. If this map
            /// contains any keys that the topic tokens specified in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="commandTimeout">How long the command will be available on the broker for an executor to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            /// <returns>The command response.</returns>
            public RpcCallAsync<SetNotificationPreferenceForDeviceUpdatesResponsePayload> SetNotificationPreferenceForDeviceUpdatesAsync(SetNotificationPreferenceForDeviceUpdatesRequestPayload request, CommandRequestMetadata? requestMetadata = null, Dictionary<string, string>? additionalTopicTokenMap = null, TimeSpan? commandTimeout = default, CancellationToken cancellationToken = default)
            {
                string? clientId = this.mqttClient.ClientId;
                if (string.IsNullOrEmpty(clientId))
                {
                    throw new InvalidOperationException("No MQTT client Id configured. Must connect to MQTT broker before invoking command.");
                }

                CommandRequestMetadata metadata = requestMetadata ?? new CommandRequestMetadata();
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }

                prefixedAdditionalTopicTokenMap["invokerClientId"] = clientId;

                return new RpcCallAsync<SetNotificationPreferenceForDeviceUpdatesResponsePayload>(this.SetNotificationPreferenceForDeviceUpdatesInt(request, metadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken), metadata.CorrelationId);
            }

            /// <summary>
            /// Invoke a command.
            /// </summary>
            /// <param name="request">The data for this command request.</param>
            /// <param name="requestMetadata">The metadata for this command request.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic tokens specified in the constructor. If this map
            /// contains any keys that the topic tokens specified in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="commandTimeout">How long the command will be available on the broker for an executor to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            /// <returns>The command response.</returns>
            public RpcCallAsync<SetNotificationPreferenceForAssetUpdatesResponsePayload> SetNotificationPreferenceForAssetUpdatesAsync(SetNotificationPreferenceForAssetUpdatesRequestPayload request, CommandRequestMetadata? requestMetadata = null, Dictionary<string, string>? additionalTopicTokenMap = null, TimeSpan? commandTimeout = default, CancellationToken cancellationToken = default)
            {
                string? clientId = this.mqttClient.ClientId;
                if (string.IsNullOrEmpty(clientId))
                {
                    throw new InvalidOperationException("No MQTT client Id configured. Must connect to MQTT broker before invoking command.");
                }

                CommandRequestMetadata metadata = requestMetadata ?? new CommandRequestMetadata();
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }

                prefixedAdditionalTopicTokenMap["invokerClientId"] = clientId;

                return new RpcCallAsync<SetNotificationPreferenceForAssetUpdatesResponsePayload>(this.SetNotificationPreferenceForAssetUpdatesInt(request, metadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken), metadata.CorrelationId);
            }

            /// <summary>
            /// Invoke a command.
            /// </summary>
            /// <param name="request">The data for this command request.</param>
            /// <param name="requestMetadata">The metadata for this command request.</param>
            /// <param name="additionalTopicTokenMap">
            /// The topic token replacement map to use in addition to the topic tokens specified in the constructor. If this map
            /// contains any keys that the topic tokens specified in the constructor also has, then values specified in this map will take precedence.
            /// </param>
            /// <param name="commandTimeout">How long the command will be available on the broker for an executor to receive.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            /// <returns>The command response.</returns>
            public RpcCallAsync<CreateOrUpdateDiscoveredAssetResponsePayload> CreateOrUpdateDiscoveredAssetAsync(CreateOrUpdateDiscoveredAssetRequestPayload request, CommandRequestMetadata? requestMetadata = null, Dictionary<string, string>? additionalTopicTokenMap = null, TimeSpan? commandTimeout = default, CancellationToken cancellationToken = default)
            {
                string? clientId = this.mqttClient.ClientId;
                if (string.IsNullOrEmpty(clientId))
                {
                    throw new InvalidOperationException("No MQTT client Id configured. Must connect to MQTT broker before invoking command.");
                }

                CommandRequestMetadata metadata = requestMetadata ?? new CommandRequestMetadata();
                additionalTopicTokenMap ??= new();

                Dictionary<string, string> prefixedAdditionalTopicTokenMap = new();
                foreach (string key in additionalTopicTokenMap.Keys)
                {
                    prefixedAdditionalTopicTokenMap["ex:" + key] = additionalTopicTokenMap[key];
                }

                prefixedAdditionalTopicTokenMap["invokerClientId"] = clientId;

                return new RpcCallAsync<CreateOrUpdateDiscoveredAssetResponsePayload>(this.CreateOrUpdateDiscoveredAssetInt(request, metadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken), metadata.CorrelationId);
            }

            /// <summary>
            /// Begin accepting telemetry for all telemetry receivers.
            /// </summary>
            /// <param name="cancellationToken">Cancellation token.</param>
            public async Task StartAsync(CancellationToken cancellationToken = default)
            {
                await Task.WhenAll(
                    this.deviceEndpointRuntimeHealthEventTelemetryReceiver.StartAsync(cancellationToken),
                    this.datasetRuntimeHealthEventTelemetryReceiver.StartAsync(cancellationToken),
                    this.eventRuntimeHealthEventTelemetryReceiver.StartAsync(cancellationToken),
                    this.managementActionRuntimeHealthEventTelemetryReceiver.StartAsync(cancellationToken),
                    this.streamRuntimeHealthEventTelemetryReceiver.StartAsync(cancellationToken),
                    this.deviceUpdateEventTelemetryReceiver.StartAsync(cancellationToken),
                    this.assetUpdateEventTelemetryReceiver.StartAsync(cancellationToken)).ConfigureAwait(false);
            }

            /// <summary>
            /// Stop accepting telemetry for all telemetry receivers and make all command invokers unsubscribe from command topics.
            /// </summary>
            /// <param name="cancellationToken">Cancellation token.</param>
            public async Task StopAsync(CancellationToken cancellationToken = default)
            {
                await Task.WhenAll(
                    this.deviceEndpointRuntimeHealthEventTelemetryReceiver.StopAsync(cancellationToken),
                    this.datasetRuntimeHealthEventTelemetryReceiver.StopAsync(cancellationToken),
                    this.eventRuntimeHealthEventTelemetryReceiver.StopAsync(cancellationToken),
                    this.managementActionRuntimeHealthEventTelemetryReceiver.StopAsync(cancellationToken),
                    this.streamRuntimeHealthEventTelemetryReceiver.StopAsync(cancellationToken),
                    this.deviceUpdateEventTelemetryReceiver.StopAsync(cancellationToken),
                    this.assetUpdateEventTelemetryReceiver.StopAsync(cancellationToken),
                    this.getDeviceCommandInvoker.StopAsync(cancellationToken),
                    this.getDeviceStatusCommandInvoker.StopAsync(cancellationToken),
                    this.getAssetCommandInvoker.StopAsync(cancellationToken),
                    this.getAssetStatusCommandInvoker.StopAsync(cancellationToken),
                    this.updateDeviceStatusCommandInvoker.StopAsync(cancellationToken),
                    this.updateAssetStatusCommandInvoker.StopAsync(cancellationToken),
                    this.setNotificationPreferenceForDeviceUpdatesCommandInvoker.StopAsync(cancellationToken),
                    this.setNotificationPreferenceForAssetUpdatesCommandInvoker.StopAsync(cancellationToken),
                    this.createOrUpdateDiscoveredAssetCommandInvoker.StopAsync(cancellationToken)).ConfigureAwait(false);
            }

            private async Task<ExtendedResponse<GetDeviceResponsePayload>> GetDeviceInt(EmptyJson request, CommandRequestMetadata? requestMetadata, Dictionary<string, string>? prefixedAdditionalTopicTokenMap, TimeSpan? commandTimeout, CancellationToken cancellationToken)
            {
                ExtendedResponse<GetDeviceResponseSchema> extended = await this.getDeviceCommandInvoker.InvokeCommandAsync(request, requestMetadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken);

                if (extended.Response.GetDeviceError != null)
                {
                    AkriServiceErrorException akriServiceErrorException = new AkriServiceErrorException(extended.Response.GetDeviceError);
                    throw akriServiceErrorException;
                }
                else
                {
                    return new ExtendedResponse<GetDeviceResponsePayload>
                    {
                        Response = new GetDeviceResponsePayload
                        {
                            Device = extended.Response.Device.Value(),
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
            }

            private async Task<ExtendedResponse<GetDeviceStatusResponsePayload>> GetDeviceStatusInt(EmptyJson request, CommandRequestMetadata? requestMetadata, Dictionary<string, string>? prefixedAdditionalTopicTokenMap, TimeSpan? commandTimeout, CancellationToken cancellationToken)
            {
                ExtendedResponse<GetDeviceStatusResponseSchema> extended = await this.getDeviceStatusCommandInvoker.InvokeCommandAsync(request, requestMetadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken);

                if (extended.Response.GetDeviceStatusError != null)
                {
                    AkriServiceErrorException akriServiceErrorException = new AkriServiceErrorException(extended.Response.GetDeviceStatusError);
                    throw akriServiceErrorException;
                }
                else
                {
                    return new ExtendedResponse<GetDeviceStatusResponsePayload>
                    {
                        Response = new GetDeviceStatusResponsePayload
                        {
                            DeviceStatus = extended.Response.DeviceStatus.Value(),
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
            }

            private async Task<ExtendedResponse<GetAssetResponsePayload>> GetAssetInt(GetAssetRequestPayload request, CommandRequestMetadata? requestMetadata, Dictionary<string, string>? prefixedAdditionalTopicTokenMap, TimeSpan? commandTimeout, CancellationToken cancellationToken)
            {
                ExtendedResponse<GetAssetResponseSchema> extended = await this.getAssetCommandInvoker.InvokeCommandAsync(request, requestMetadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken);

                if (extended.Response.GetAssetError != null)
                {
                    AkriServiceErrorException akriServiceErrorException = new AkriServiceErrorException(extended.Response.GetAssetError);
                    throw akriServiceErrorException;
                }
                else
                {
                    return new ExtendedResponse<GetAssetResponsePayload>
                    {
                        Response = new GetAssetResponsePayload
                        {
                            Asset = extended.Response.Asset.Value(),
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
            }

            private async Task<ExtendedResponse<GetAssetStatusResponsePayload>> GetAssetStatusInt(GetAssetStatusRequestPayload request, CommandRequestMetadata? requestMetadata, Dictionary<string, string>? prefixedAdditionalTopicTokenMap, TimeSpan? commandTimeout, CancellationToken cancellationToken)
            {
                ExtendedResponse<GetAssetStatusResponseSchema> extended = await this.getAssetStatusCommandInvoker.InvokeCommandAsync(request, requestMetadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken);

                if (extended.Response.GetAssetStatusError != null)
                {
                    AkriServiceErrorException akriServiceErrorException = new AkriServiceErrorException(extended.Response.GetAssetStatusError);
                    throw akriServiceErrorException;
                }
                else
                {
                    return new ExtendedResponse<GetAssetStatusResponsePayload>
                    {
                        Response = new GetAssetStatusResponsePayload
                        {
                            AssetStatus = extended.Response.AssetStatus.Value(),
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
            }

            private async Task<ExtendedResponse<UpdateDeviceStatusResponsePayload>> UpdateDeviceStatusInt(UpdateDeviceStatusRequestPayload request, CommandRequestMetadata? requestMetadata, Dictionary<string, string>? prefixedAdditionalTopicTokenMap, TimeSpan? commandTimeout, CancellationToken cancellationToken)
            {
                ExtendedResponse<UpdateDeviceStatusResponseSchema> extended = await this.updateDeviceStatusCommandInvoker.InvokeCommandAsync(request, requestMetadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken);

                if (extended.Response.UpdateDeviceStatusError != null)
                {
                    AkriServiceErrorException akriServiceErrorException = new AkriServiceErrorException(extended.Response.UpdateDeviceStatusError);
                    throw akriServiceErrorException;
                }
                else
                {
                    return new ExtendedResponse<UpdateDeviceStatusResponsePayload>
                    {
                        Response = new UpdateDeviceStatusResponsePayload
                        {
                            UpdatedDeviceStatus = extended.Response.UpdatedDeviceStatus.Value(),
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
            }

            private async Task<ExtendedResponse<UpdateAssetStatusResponsePayload>> UpdateAssetStatusInt(UpdateAssetStatusRequestPayload request, CommandRequestMetadata? requestMetadata, Dictionary<string, string>? prefixedAdditionalTopicTokenMap, TimeSpan? commandTimeout, CancellationToken cancellationToken)
            {
                ExtendedResponse<UpdateAssetStatusResponseSchema> extended = await this.updateAssetStatusCommandInvoker.InvokeCommandAsync(request, requestMetadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken);

                if (extended.Response.UpdateAssetStatusError != null)
                {
                    AkriServiceErrorException akriServiceErrorException = new AkriServiceErrorException(extended.Response.UpdateAssetStatusError);
                    throw akriServiceErrorException;
                }
                else
                {
                    return new ExtendedResponse<UpdateAssetStatusResponsePayload>
                    {
                        Response = new UpdateAssetStatusResponsePayload
                        {
                            UpdatedAssetStatus = extended.Response.UpdatedAssetStatus.Value(),
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
            }

            private async Task<ExtendedResponse<SetNotificationPreferenceForDeviceUpdatesResponsePayload>> SetNotificationPreferenceForDeviceUpdatesInt(SetNotificationPreferenceForDeviceUpdatesRequestPayload request, CommandRequestMetadata? requestMetadata, Dictionary<string, string>? prefixedAdditionalTopicTokenMap, TimeSpan? commandTimeout, CancellationToken cancellationToken)
            {
                ExtendedResponse<SetNotificationPreferenceForDeviceUpdatesResponseSchema> extended = await this.setNotificationPreferenceForDeviceUpdatesCommandInvoker.InvokeCommandAsync(request, requestMetadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken);

                if (extended.Response.SetNotificationPreferenceForDeviceUpdatesError != null)
                {
                    AkriServiceErrorException akriServiceErrorException = new AkriServiceErrorException(extended.Response.SetNotificationPreferenceForDeviceUpdatesError);
                    throw akriServiceErrorException;
                }
                else
                {
                    return new ExtendedResponse<SetNotificationPreferenceForDeviceUpdatesResponsePayload>
                    {
                        Response = new SetNotificationPreferenceForDeviceUpdatesResponsePayload
                        {
                            ResponsePayload = extended.Response.ResponsePayload.Value(),
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
            }

            private async Task<ExtendedResponse<SetNotificationPreferenceForAssetUpdatesResponsePayload>> SetNotificationPreferenceForAssetUpdatesInt(SetNotificationPreferenceForAssetUpdatesRequestPayload request, CommandRequestMetadata? requestMetadata, Dictionary<string, string>? prefixedAdditionalTopicTokenMap, TimeSpan? commandTimeout, CancellationToken cancellationToken)
            {
                ExtendedResponse<SetNotificationPreferenceForAssetUpdatesResponseSchema> extended = await this.setNotificationPreferenceForAssetUpdatesCommandInvoker.InvokeCommandAsync(request, requestMetadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken);

                if (extended.Response.SetNotificationPreferenceForAssetUpdatesError != null)
                {
                    AkriServiceErrorException akriServiceErrorException = new AkriServiceErrorException(extended.Response.SetNotificationPreferenceForAssetUpdatesError);
                    throw akriServiceErrorException;
                }
                else
                {
                    return new ExtendedResponse<SetNotificationPreferenceForAssetUpdatesResponsePayload>
                    {
                        Response = new SetNotificationPreferenceForAssetUpdatesResponsePayload
                        {
                            ResponsePayload = extended.Response.ResponsePayload.Value(),
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
            }

            private async Task<ExtendedResponse<CreateOrUpdateDiscoveredAssetResponsePayload>> CreateOrUpdateDiscoveredAssetInt(CreateOrUpdateDiscoveredAssetRequestPayload request, CommandRequestMetadata? requestMetadata, Dictionary<string, string>? prefixedAdditionalTopicTokenMap, TimeSpan? commandTimeout, CancellationToken cancellationToken)
            {
                ExtendedResponse<CreateOrUpdateDiscoveredAssetResponseSchema> extended = await this.createOrUpdateDiscoveredAssetCommandInvoker.InvokeCommandAsync(request, requestMetadata, prefixedAdditionalTopicTokenMap, commandTimeout, cancellationToken);

                if (extended.Response.CreateOrUpdateDiscoveredAssetError != null)
                {
                    AkriServiceErrorException akriServiceErrorException = new AkriServiceErrorException(extended.Response.CreateOrUpdateDiscoveredAssetError);
                    throw akriServiceErrorException;
                }
                else
                {
                    return new ExtendedResponse<CreateOrUpdateDiscoveredAssetResponsePayload>
                    {
                        Response = new CreateOrUpdateDiscoveredAssetResponsePayload
                        {
                            DiscoveredAssetResponse = extended.Response.DiscoveredAssetResponse.Value(),
                        },
                        ResponseMetadata = extended.ResponseMetadata,
                    };
                }
            }

            public async ValueTask DisposeAsync()
            {
                await this.getDeviceCommandInvoker.DisposeAsync().ConfigureAwait(false);
                await this.getDeviceStatusCommandInvoker.DisposeAsync().ConfigureAwait(false);
                await this.getAssetCommandInvoker.DisposeAsync().ConfigureAwait(false);
                await this.getAssetStatusCommandInvoker.DisposeAsync().ConfigureAwait(false);
                await this.updateDeviceStatusCommandInvoker.DisposeAsync().ConfigureAwait(false);
                await this.updateAssetStatusCommandInvoker.DisposeAsync().ConfigureAwait(false);
                await this.setNotificationPreferenceForDeviceUpdatesCommandInvoker.DisposeAsync().ConfigureAwait(false);
                await this.setNotificationPreferenceForAssetUpdatesCommandInvoker.DisposeAsync().ConfigureAwait(false);
                await this.createOrUpdateDiscoveredAssetCommandInvoker.DisposeAsync().ConfigureAwait(false);
                await this.deviceEndpointRuntimeHealthEventTelemetryReceiver.DisposeAsync().ConfigureAwait(false);
                await this.datasetRuntimeHealthEventTelemetryReceiver.DisposeAsync().ConfigureAwait(false);
                await this.eventRuntimeHealthEventTelemetryReceiver.DisposeAsync().ConfigureAwait(false);
                await this.managementActionRuntimeHealthEventTelemetryReceiver.DisposeAsync().ConfigureAwait(false);
                await this.streamRuntimeHealthEventTelemetryReceiver.DisposeAsync().ConfigureAwait(false);
                await this.deviceUpdateEventTelemetryReceiver.DisposeAsync().ConfigureAwait(false);
                await this.assetUpdateEventTelemetryReceiver.DisposeAsync().ConfigureAwait(false);
            }

            public async ValueTask DisposeAsync(bool disposing, CancellationToken cancellationToken = default)
            {
                await this.getDeviceCommandInvoker.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.getDeviceStatusCommandInvoker.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.getAssetCommandInvoker.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.getAssetStatusCommandInvoker.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.updateDeviceStatusCommandInvoker.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.updateAssetStatusCommandInvoker.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.setNotificationPreferenceForDeviceUpdatesCommandInvoker.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.setNotificationPreferenceForAssetUpdatesCommandInvoker.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.createOrUpdateDiscoveredAssetCommandInvoker.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.deviceEndpointRuntimeHealthEventTelemetryReceiver.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.datasetRuntimeHealthEventTelemetryReceiver.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.eventRuntimeHealthEventTelemetryReceiver.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.managementActionRuntimeHealthEventTelemetryReceiver.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.streamRuntimeHealthEventTelemetryReceiver.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.deviceUpdateEventTelemetryReceiver.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
                await this.assetUpdateEventTelemetryReceiver.DisposeAsync(disposing, cancellationToken).ConfigureAwait(false);
            }
        }
    }
}
