IoTHubClientGenerator 1.1.4

dotnet add package IoTHubClientGenerator --version 1.1.4
NuGet\Install-Package IoTHubClientGenerator -Version 1.1.4
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="IoTHubClientGenerator" Version="1.1.4" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add IoTHubClientGenerator --version 1.1.4
#r "nuget: IoTHubClientGenerator, 1.1.4"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install IoTHubClientGenerator as a Cake Addin
#addin nuget:?package=IoTHubClientGenerator&version=1.1.4

// Install IoTHubClientGenerator as a Cake Tool
#tool nuget:?package=IoTHubClientGenerator&version=1.1.4

IoT Hub Client C# code generator.

To get started, follow this walk-through

This project uses the new C# ability to generate code as part of the C# code compilation process. This code generator can build an IoT Device client program in seconds. For example, the following code creates a device client that can send telemetry, receive commands, update twin reported properties, get desired twin properties updates, get the current connection state, and handle direct method calls:

namespace EasyIoTHubClient
{
    [IoTHub(GeneratedSendMethodName = "SendAsync")]
    partial class IoTDemo
    {
        [Reported("BatteryLevel")] private string _batteryLevel;

        [Desired] private int ReportingFrequencyInHz { get; set; } = 1;

        static async Task Main(string[] args)
        {
            var iotDemo = new IoTDemo();
            await iotDemo.InitIoTHubClientAsync();
            iotDemo.BatteryLevel = "100%";
            await iotDemo.SendDataAsync();
        }

        private async Task SendDataAsync()
        {
            for (int i = 1000; i >= 0; --i)
            {
                BatteryLevel = $"{i % 100}%";
                await SendAsync($"{{\"data\":\"{i}\"", i.ToString(), new CancellationToken());
                await Task.Delay(TimeSpan.FromMilliseconds(1000.0 / ReportingFrequencyInHz));
            }
        }

        [C2DMessage(AutoComplete = true)]
        private void Cloud2DeviceMessage(Message receivedMessage)
        {
            string messageData = Encoding.ASCII.GetString(receivedMessage.GetBytes());
            Console.WriteLine($"Received message: [{messageData}]");
        }

        [DirectMethod]
        private Task<MethodResponse> EchoAsync(MethodRequest request)
        {
            var response = new MethodResponse(request.Data, 200);
            return Task.FromResult(response);
        }


        [ConnectionStatus]
        private (ConnectionStatus Status, ConnectionStatusChangeReason Reason) DeviceConnectionStatus { get; set; }
        
        [IoTHubErrorHandler]
        void IoTHubErrorHandler(string errorMessage, Exception exception)
        {
            Console.WriteLine($"{errorMessage}, Exception: {exception.Message}");
        }
    }
}

Advanced Features

You may use one of the [Device] or the [DPS*] attributes to decorate a DeviceClient property. We can manipulate the IoT Hub device client creation parameters with these attributes and properties. Each property value can be set as a text or can be set as an environment variable value by wrapping the value with the % character, for example:

[Device(ConnectionString="%ConStr%")]
DeviceClient MyClient {get;set;}

Each property value can be set as a variable name or code expression by wrapping the value with the [varName] character, for example:

[Device(ConnectionString="[ConStr]")]
DeviceClient MyClient {get;set;}

The [Device] attribute has a long list of properties and a set of other attributes ([ClientOptions], [TransportSetting], and [AuthenticationMethod]) that create the parameter of the IoT device client Create method. The code generator chooses the correct overload version of the device client Create() function by collecting all these parameters and selecting the suitable function version. The code generator emits an error if there is a missing parameter or a collision between parameters. Example of non-trivial device creation:

    [Device(ConnectionString = "%conString%", DeviceId = "%deviceId%")]
    public DeviceClient DeviceClient { get; set; }

    [TransportSetting]
    public ITransportSettings AmqpTransportSettings { get; } = new AmqpTransportSettings(TransportType.Amqp)
    {
        AmqpConnectionPoolSettings = new AmqpConnectionPoolSettings {MaxPoolSize = 5}, IdleTimeout = TimeSpan.FromMinutes(1)
    };

    [TransportSetting]
    public ITransportSettings MqttTransportSetting { get; } = new MqttTransportSettings(TransportType.Mqtt)
    {
        DefaultReceiveTimeout = TimeSpan.FromMinutes(2)
    };

    [ClientOptions]
    public ClientOptions ClientOptions { get; } = new();

The following code is the result of the example:

private Microsoft.Azure.Devices.Client.DeviceClient CreateDeviceClient()
{
    var theConnectionString = System.Environment.GetEnvironmentVariable("conString");
    var theDeviceId = System.Environment.GetEnvironmentVariable("deviceId");
    ITransportSettings[] transportSettings = new[]{AmqpTransportSettings, MqttTransportSetting};
    var deviceClient = DeviceClient.CreateFromConnectionString(theConnectionString, theDeviceId, transportSettings, ClientOptions);
    return deviceClient;
}

Or, if you want to set the authentication method:

    [Device(Hostname = "%hostName%", TransportType = TransportType.Mqtt)]
    public DeviceClient DeviceClient { get; set; }

    [AuthenticationMethod]
    public IAuthenticationMethod DeviceAuthenticationWithRegistrySymmetricKey { get; } = new DeviceAuthenticationWithRegistrySymmetricKey("deviceId", "key");

And the result generated code is:

private Microsoft.Azure.Devices.Client.DeviceClient CreateDeviceClient()
{
    var theHostname = System.Environment.GetEnvironmentVariable("hostName");
    var theTransportType = TransportType.Mqtt;
    var deviceClient = DeviceClient.Create(theHostname, DeviceAuthenticationWithRegistrySymmetricKey, theTransportType);
    return deviceClient;
}

To use the Device Provisioning Service of Azure IoT, decorate the Device Client property with one of [DpsSymmetricKeyDevice], [DpsTpmDevice], [DpsX509CertificateDevice]

Example:

        [DpsSymmetricKeyDevice(DPSIdScope="scope", DPSTransportType=TransportType.Mqtt, TransportType=TransportType.Mqtt,
            EnrollmentGroupId="%EnrollmentGroupId%", EnrollmentType=DPSEnrollmentType.Group, 
            Id="%RegistrationId%", PrimarySymmetricKey="%SymKey%")]
        public DeviceClient DeviceClient { get; set; }

To compile the code, we need to add the NuGet Packages: Microsoft.Azure.Devices.Provisioning.Client and Microsoft.Azure.Devices.Provisioning.Transport.Mqtt

The resulting generated code:

    private async Task<Microsoft.Azure.Devices.Client.DeviceClient> CreateDeviceClientAsync()
    {
        var theDPSIdScope = "scope";
        var theDPSTransportType = TransportType.Mqtt;
        var theTransportType = TransportType.Mqtt;
        var theEnrollmentGroupId = System.Environment.GetEnvironmentVariable("EnrollmentGroupId");
        var theEnrollmentType = DPSEnrollmentType.Group;
        var theId = System.Environment.GetEnvironmentVariable("RegistrationId");
        var thePrimarySymmetricKey = System.Environment.GetEnvironmentVariable("SymKey");
        using var security = new SecurityProviderSymmetricKey(theId, thePrimarySymmetricKey, null);
        using var transport = new ProvisioningTransportHandlerMqtt();
        var theGlobalDeviceEndpoint = "global.azure-devices-provisioning.net";
        ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create(theGlobalDeviceEndpoint, theDPSIdScope, security, transport);
        DeviceRegistrationResult result = await provClient.RegisterAsync();
        if (result.Status != ProvisioningRegistrationStatusType.Assigned)
        {
            throw new Exception($"Registration status did not assign a hub, status: {result.Status}");
        }

        IAuthenticationMethod auth = new DeviceAuthenticationWithRegistrySymmetricKey(result.DeviceId, security.GetPrimaryKey());
        var deviceClient = DeviceClient.Create(result.AssignedHub, auth, theTransportType);
        return deviceClient;
    }

IoTHubClientGeneratorSDK Namespace

Classes

Enums

Missing features and known issues:

  • The Source Generator uses the old ISourceGenerator interface and does not benefit from the .NET 6.0 and VS 2022 incremental generator. While it enables using VS2019, the new approach provides much better performance.
  • Move some implementations to the semantic data, not the syntax tree. It will solve several issues, such as spaces around =
  • Have a separate error handler for each attribute instead (or as an override rule) of one global handler
  • Add the ability for the user to provide the MessageSchema ContentType and ContentEncoding to the send-telemetry method in compile and at runtime
  • Can postpone update of reported properties, and only after setting a group of them ask for a batch update of all
  • Add support for:
    • File Uploads
    • Device Modules
    • Device Streams
There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.4 224 9/9/2023
1.1.2 196 3/22/2023
1.1.0 195 3/22/2023
1.0.1 479 6/11/2022
1.0.0 468 6/11/2022
0.1.9 466 3/22/2021
0.1.8 473 1/9/2021
0.1.7 494 1/6/2021
0.1.6 456 1/6/2021
0.1.5 461 12/30/2020
0.1.4 485 12/28/2020
0.1.3 481 12/28/2020
0.1.2 406 12/28/2020
0.1.1 450 12/28/2020