Azure-iot-sdk-csharp: System.NotSupportedException: Specified method is not supported on .NET5.0 RC1

Created on 17 Sep 2020  路  6Comments  路  Source: Azure/azure-iot-sdk-csharp

Context

  • OS, version, SKU and CPU architecture used: Windows 10 Desktop x64 v2004 19041.508
  • Application's .NET Target Framework : net5.0
  • Device: Desktop PC
  • SDK version used: 1.30.0

Description of the issue

A console application was created with dotnet new console and targeted net5.0. When sending an event await deviceClient.SendEventAsync(message);, the following exception is thrown System.NotSupportedException: Specified method is not supported. Changing the console target to netcoreapp3.1 results in a successful execution.

Code sample exhibiting the issue

deviceClient = DeviceClient.CreateFromConnectionString(connectionString, TransportType.Mqtt);
var messageString = CreateMessageString(currentTemperature, currentHumidity);

 // create a byte array from the message string using ASCII encoding
var message = new Message(Encoding.ASCII.GetBytes(messageString));

// Add a custom application property to the message.
// An IoT hub can filter on these properties without access to the message body.
message.Properties.Add("temperatureAlert", (currentTemperature > 30) ? "true" : "false");

// Send the telemetry message
await deviceClient.SendEventAsync(message);

Console log of the issue

Unhandled exception. System.NotSupportedException: Specified method is not supported.
   at Microsoft.Azure.Devices.Client.Common.ReadOnlyMergeDictionary`2.System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>.get_Count()
   at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source)
   at Microsoft.Azure.Devices.Client.Common.UrlEncodedDictionarySerializer.Serialize(IEnumerable`1 properties)
   at Microsoft.Azure.Devices.Client.Transport.Mqtt.MqttIotHubAdapter.PopulateMessagePropertiesFromMessage(String topicName, Message message)
   at Microsoft.Azure.Devices.Client.Transport.Mqtt.MqttIotHubAdapter.ComposePublishPacketAsync(IChannelHandlerContext context, Message message, QualityOfService qos, String topicName)
   at Microsoft.Azure.Devices.Client.Transport.Mqtt.MqttIotHubAdapter.SendMessageAsync(IChannelHandlerContext context, Message message)
   at Microsoft.Azure.Devices.Client.Transport.Mqtt.MqttIotHubAdapter.WriteAsync(IChannelHandlerContext context, Object data)
   at Microsoft.Azure.Devices.Client.Transport.Mqtt.MqttTransportHandler.SendEventAsync(Message message, CancellationToken cancellationToken)
   at Microsoft.Azure.Devices.Client.Transport.ErrorDelegatingHandler.<>c__DisplayClass23_0.<<ExecuteWithErrorHandlingAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.Azure.Devices.Client.Transport.ErrorDelegatingHandler.ExecuteWithErrorHandlingAsync[T](Func`1 asyncOperation)
   at Microsoft.Azure.Devices.Client.Transport.RetryDelegatingHandler.<>c__DisplayClass14_0.<<SendEventAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.Azure.Devices.Client.Transport.RetryDelegatingHandler.SendEventAsync(Message message, CancellationToken cancellationToken)
   at Microsoft.Azure.Devices.Client.InternalClient.SendEventAsync(Message message)
   at CaveDevice.Program.SendDeviceToCloudMessagesAsync() in D:\D-Repos\MSLearn-IoTDeveloper\2 - IoT Device registration and Provisioning\4 - Connect IoT Device to Azure\CaveDevice\Program.cs:line 55
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__140_1(Object state)
   at System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

VsCodeError

IoTSDK bug fix-checked-in

Most helpful comment

@timtay-microsoft

Our device client library currently only supports netstandard2.1, netstandard2.0, net472, and net451. We'll consider this a feature request, though

That is not how you should design packages. Packages should always be built with forward compatibility in mind. For examle, when your package is built for netstandard2.0 then customers who are on netstandard2.1 should be able consume the package, unless the platform made a breaking change that impacts your package.

We designed net5.0 in such a way that NuGet treats is as a higher version of netcoreapp3.1. It seems this logic in your MSBuild targets prevents this from working:

<PropertyGroup>
    <SupportsNetStandard20AndAbove Condition="'$(TargetFramework)' == 'netstandard2.1' Or '$(TargetFramework)' == 'netstandard2.0' Or '$(TargetFramework)' == 'net472'">true</SupportsNetStandard20AndAbove>
  </PropertyGroup>

What are you trying to achieve by this method? Maybe there is less fragile way to do that.

All 6 comments

Our device client library currently only supports netstandard2.1, netstandard2.0, net472, and net451. We'll consider this a feature request, though

Thanks for the response. I believe there is going to be so much confusion moving forward... Someone installs .NET5.0 and follows lab steps:

  • Run dotnet new console
  • Run dotnet add package Microsoft.Azure.Devices.Client

    • No errors or warnings

      dotnet add package Microsoft.Azure.Devices.Client Determining projects to restore... Writing C:\Users\daren\AppData\Local\Temp\tmpA3A6.tmp info : Adding PackageReference for package 'Microsoft.Azure.Devices.Client' into project 'D:\D-Repos\MSLearn-IoTDeveloper\2 - IoT Device registration and Provisioning\4 - Connect IoT Device to Azure\CaveDevice\CaveDevice.csproj'. info : GET https://api.nuget.org/v3/registration5-gz-semver2/microsoft.azure.devices.client/index.json info : OK https://api.nuget.org/v3/registration5-gz-semver2/microsoft.azure.devices.client/index.json 133ms info : Restoring packages for D:\D-Repos\MSLearn-IoTDeveloper\2 - IoT Device registration and Provisioning\4 - Connect IoT Device to Azure\CaveDevice\CaveDevice.csproj... info : GET https://api.nuget.org/v3-flatcontainer/microsoft.azure.devices.client/index.json info : OK https://api.nuget.org/v3-flatcontainer/microsoft.azure.devices.client/index.json 119ms info : GET https://api.nuget.org/v3-flatcontainer/microsoft.azure.devices.client/1.30.0/microsoft.azure.devices.client.1.30.0.nupkg info : OK https://api.nuget.org/v3-flatcontainer/microsoft.azure.devices.client/1.30.0/microsoft.azure.devices.client.1.30.0.nupkg 163ms info : Installing Microsoft.Azure.Devices.Client 1.30.0. info : Package 'Microsoft.Azure.Devices.Client' is compatible with all the specified frameworks in project 'D:\D-Repos\MSLearn- IoTDeveloper\2 - IoT Device registration and Provisioning\4 - Connect IoT Device to Azure\CaveDevice\CaveDevice.csproj'. info : PackageReference for package 'Microsoft.Azure.Devices.Client' version '1.30.0' added to file 'D:\D-Repos\MSLearn- IoTDeveloper\2 - IoT Device registration and Provisioning\4 - Connect IoT Device to Azure\CaveDevice\CaveDevice.csproj'. info : Committing restore... info : Writing assets file to disk. Path: D:\D-Repos\MSLearn-IoTDeveloper\2 - IoT Device registration and Provisioning\4 - Connect IoT Device to Azure\CaveDevice\obj\project.assets.json log : Restored D:\D-Repos\MSLearn-IoTDeveloper\2 - IoT Device registration and Provisioning\4 - Connect IoT Device to Azure\CaveDevice\CaveDevice.csproj (in 1.73 sec).

  • Add code to Project.cs
  • Run dotnet build

    • Everything compiles no problem

  • Run dotnet run

    • Runtime error

It is especially confusing given that:

.NET 5 and all future versions will always support .NET Standard 2.1 and earlier. The only reason to retarget from .NET Standard to .NET 5 is to gain access to more runtime features, language features, or APIs. So, you can think of .NET 5 as .NET Standard vNext. The future of .NET Standard

@timtay-microsoft

Our device client library currently only supports netstandard2.1, netstandard2.0, net472, and net451. We'll consider this a feature request, though

That is not how you should design packages. Packages should always be built with forward compatibility in mind. For examle, when your package is built for netstandard2.0 then customers who are on netstandard2.1 should be able consume the package, unless the platform made a breaking change that impacts your package.

We designed net5.0 in such a way that NuGet treats is as a higher version of netcoreapp3.1. It seems this logic in your MSBuild targets prevents this from working:

<PropertyGroup>
    <SupportsNetStandard20AndAbove Condition="'$(TargetFramework)' == 'netstandard2.1' Or '$(TargetFramework)' == 'netstandard2.0' Or '$(TargetFramework)' == 'net472'">true</SupportsNetStandard20AndAbove>
  </PropertyGroup>

What are you trying to achieve by this method? Maybe there is less fragile way to do that.

Your point is valid, this expression in our csproj files is used to manage the library's cross-tfm dependencies; but I agree, we will update it to be forward compatible (a reversal of the expression should take care of that for us).
I would still like to state that, until we explicitly declare compatibility with a newer version of .NET, we wouldn't call it out that we support the platform. However, the update in the csproj should unblock you from testing your application for compatibility.

@timtay-microsoft, @darenm, @terrajobst, @abhipsaMisra, thank you for your contribution to our open-sourced project! Please help us improve by filling out this 2-minute customer satisfaction survey

Was this page helpful?
0 / 5 - 0 ratings