From 956b5e672be3afc80910c7d650c5786107bc773b Mon Sep 17 00:00:00 2001 From: campersau Date: Mon, 23 Mar 2026 20:43:57 +0100 Subject: [PATCH 1/6] Enable IsAotCompatible --- .github/workflows/ci.yml | 10 +- Directory.Packages.props | 5 +- global.json | 5 + src/Directory.Build.props | 1 + .../DockerHandlerFactory.cs | 6 +- .../DockerHandlerFactory.cs | 2 +- .../NativeHttpTransportOptions.cs | 2 +- .../CertificateCredentials.cs | 2 +- .../DockerTlsCertificates.cs | 8 +- src/Docker.DotNet/Docker.DotNet.csproj | 1 + src/Docker.DotNet/DockerClient.cs | 2 +- .../IQueryStringConverterInstanceFactory.cs | 6 +- src/Docker.DotNet/JsonSerializer.cs | 27 +- ...erModelsJsonSerializerContext.Generated.cs | 300 ++++++++++++++++++ src/Docker.DotNet/QueryString.cs | 15 +- .../QueryStringConverterInstanceFactory.cs | 26 +- .../QueryStringParameterAttribute.cs | 11 +- .../HttpConnection.cs | 4 +- .../HttpConnectionResponseContent.cs | 2 +- .../RequestExtensions.cs | 4 +- .../Docker.DotNet.Tests.csproj | 24 +- .../IConfigOperationsTests.cs | 12 +- .../IContainerOperationsTests.cs | 30 +- .../IImageOperationsTests.cs | 5 +- .../ISwarmOperationsTests.cs | 44 +-- .../ISystemOperations.Tests.cs | 26 +- .../JsonEnumMemberConverterTest.cs | 2 +- .../JsonRequestContentTests.cs | 6 +- test/Docker.DotNet.Tests/TestFixture.cs | 4 +- .../Docker.DotNet.TestsV2.csproj | 24 +- tools/specgen/specgen.go | 31 ++ 31 files changed, 519 insertions(+), 128 deletions(-) create mode 100644 global.json create mode 100644 src/Docker.DotNet/Models/DockerModelsJsonSerializerContext.Generated.cs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0559e652..90d97e1b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,13 +132,9 @@ jobs: exit 1 - name: Test (${{ matrix.docker.name }}) - run: >- - dotnet test - --configuration Release - --framework ${{ matrix.dotnet.tfm }} - --no-restore - --no-build - --logger console + run: | + ./test/Docker.DotNet.Tests/bin/Release/${{ matrix.dotnet.tfm }}/linux-x64/publish/Docker.DotNet.Tests + ./test/Docker.DotNet.TestsV2/bin/Release/${{ matrix.dotnet.tfm }}/linux-x64/publish/Docker.DotNet.TestsV2 env: DOCKER_HOST: ${{ matrix.docker.docker_host }} DOCKER_TLS_VERIFY: ${{ matrix.docker.tls_verify }} diff --git a/Directory.Packages.props b/Directory.Packages.props index c8be7ea0..3bb23f85 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -11,8 +11,7 @@ - - - + + diff --git a/global.json b/global.json new file mode 100644 index 00000000..802ab217 --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "test": { + "runner": "Microsoft.Testing.Platform" + } +} \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props index efc57594..a6367c51 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -20,6 +20,7 @@ enable + true diff --git a/src/Docker.DotNet.NPipe/DockerHandlerFactory.cs b/src/Docker.DotNet.NPipe/DockerHandlerFactory.cs index 40d003f4..1310e069 100644 --- a/src/Docker.DotNet.NPipe/DockerHandlerFactory.cs +++ b/src/Docker.DotNet.NPipe/DockerHandlerFactory.cs @@ -45,10 +45,10 @@ public ResolvedTransport CreateHandler(NPipeTransportOptions transportOptions, C var dockerStream = new DockerPipeStream(clientStream); -#if NETSTANDARD - var namedPipeConnectTimeout = (int)transportOptions.ConnectTimeout.TotalMilliseconds; -#else +#if NET var namedPipeConnectTimeout = transportOptions.ConnectTimeout; +#else + var namedPipeConnectTimeout = (int)transportOptions.ConnectTimeout.TotalMilliseconds; #endif await clientStream.ConnectAsync(namedPipeConnectTimeout, cancellationToken) diff --git a/src/Docker.DotNet.NativeHttp/DockerHandlerFactory.cs b/src/Docker.DotNet.NativeHttp/DockerHandlerFactory.cs index 4ee2d2d1..a7c568f8 100644 --- a/src/Docker.DotNet.NativeHttp/DockerHandlerFactory.cs +++ b/src/Docker.DotNet.NativeHttp/DockerHandlerFactory.cs @@ -29,7 +29,7 @@ public ResolvedTransport CreateHandler(NativeHttpTransportOptions transportOptio var scheme = clientOptions.AuthProvider.TlsEnabled ? Uri.UriSchemeHttps : Uri.UriSchemeHttp; var uri = new UriBuilder(clientOptions.Endpoint) { Scheme = scheme }.Uri; -#if NET6_0_OR_GREATER +#if NET var handler = new SocketsHttpHandler { MaxConnectionsPerServer = MaxConnectionsPerServer, diff --git a/src/Docker.DotNet.NativeHttp/NativeHttpTransportOptions.cs b/src/Docker.DotNet.NativeHttp/NativeHttpTransportOptions.cs index 5bf408ce..45a295fa 100644 --- a/src/Docker.DotNet.NativeHttp/NativeHttpTransportOptions.cs +++ b/src/Docker.DotNet.NativeHttp/NativeHttpTransportOptions.cs @@ -5,7 +5,7 @@ namespace Docker.DotNet.NativeHttp; /// public sealed record NativeHttpTransportOptions { -#if NET6_0_OR_GREATER +#if NET /// /// Gets a callback that configures the created instance. /// diff --git a/src/Docker.DotNet.X509/CertificateCredentials.cs b/src/Docker.DotNet.X509/CertificateCredentials.cs index 1c732bbd..2b37fe04 100644 --- a/src/Docker.DotNet.X509/CertificateCredentials.cs +++ b/src/Docker.DotNet.X509/CertificateCredentials.cs @@ -15,7 +15,7 @@ public CertificateCredentials(X509Certificate2? certificate) public HttpMessageHandler ConfigureHandler(HttpMessageHandler handler) { -#if NET6_0_OR_GREATER +#if NET if (handler is SocketsHttpHandler socketsHandler) { if (_certificate != null) diff --git a/src/Docker.DotNet.X509/DockerTlsCertificates.cs b/src/Docker.DotNet.X509/DockerTlsCertificates.cs index 193fb85c..bbf58750 100644 --- a/src/Docker.DotNet.X509/DockerTlsCertificates.cs +++ b/src/Docker.DotNet.X509/DockerTlsCertificates.cs @@ -64,7 +64,7 @@ public static X509Certificate2 LoadCertificateFromPemFiles(string certPemPath, s } return certificate; -#elif NET6_0_OR_GREATER +#elif NET var certificate = X509Certificate2.CreateFromPemFile(certPemPath, keyPemPath); if (OperatingSystem.IsWindows()) @@ -75,10 +75,8 @@ public static X509Certificate2 LoadCertificateFromPemFiles(string certPemPath, s } return certificate; -#elif NETSTANDARD - return Polyfills.X509Certificate2.CreateFromPemFile(certPemPath, keyPemPath); #else - return X509Certificate2.CreateFromPemFile(certPemPath, keyPemPath); + return Polyfills.X509Certificate2.CreateFromPemFile(certPemPath, keyPemPath); #endif } @@ -141,7 +139,7 @@ public static RemoteCertificateValidationCallback CreateCertificateAuthorityVali using var chain = new X509Chain(); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; -#if NET5_0_OR_GREATER +#if NET chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; chain.ChainPolicy.CustomTrustStore.Add(certificateAuthorityCertificate); return chain.Build(serverCertificate2); diff --git a/src/Docker.DotNet/Docker.DotNet.csproj b/src/Docker.DotNet/Docker.DotNet.csproj index da5c71ad..5ce06281 100644 --- a/src/Docker.DotNet/Docker.DotNet.csproj +++ b/src/Docker.DotNet/Docker.DotNet.csproj @@ -29,6 +29,7 @@ + diff --git a/src/Docker.DotNet/DockerClient.cs b/src/Docker.DotNet/DockerClient.cs index 6754f9b6..d9f1b1b5 100644 --- a/src/Docker.DotNet/DockerClient.cs +++ b/src/Docker.DotNet/DockerClient.cs @@ -370,7 +370,7 @@ private async Task PrivateMakeRequestAsync( if (Timeout.InfiniteTimeSpan == timeout) { -#if NET6_0_OR_GREATER +#if NET return await _client.SendAsync(request, completionOption, cancellationToken) .ConfigureAwait(false); #else diff --git a/src/Docker.DotNet/IQueryStringConverterInstanceFactory.cs b/src/Docker.DotNet/IQueryStringConverterInstanceFactory.cs index cd81a9c5..a8770c15 100644 --- a/src/Docker.DotNet/IQueryStringConverterInstanceFactory.cs +++ b/src/Docker.DotNet/IQueryStringConverterInstanceFactory.cs @@ -2,5 +2,9 @@ namespace Docker.DotNet; internal interface IQueryStringConverterInstanceFactory { - IQueryStringConverter GetConverterInstance(Type t); + IQueryStringConverter GetConverterInstance( +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] +#endif + Type t); } \ No newline at end of file diff --git a/src/Docker.DotNet/JsonSerializer.cs b/src/Docker.DotNet/JsonSerializer.cs index 87e11f9b..34d5a0c3 100644 --- a/src/Docker.DotNet/JsonSerializer.cs +++ b/src/Docker.DotNet/JsonSerializer.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization.Metadata; + namespace Docker.DotNet; internal sealed class JsonSerializer @@ -15,11 +17,16 @@ static JsonSerializer() private JsonSerializer() { + _options.TypeInfoResolver = JsonTypeInfoResolver.Combine( + DockerModelsJsonSerializerContext.Default, + DockerExtendedJsonSerializerContext.Default + ); _options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; _options.Converters.Add(new JsonEnumMemberConverter()); _options.Converters.Add(new JsonEnumMemberConverter()); _options.Converters.Add(new JsonDateTimeConverter()); _options.Converters.Add(new JsonNullableDateTimeConverter()); + _options.MakeReadOnly(); } public static JsonSerializer Instance { get; } @@ -38,22 +45,22 @@ public HttpContent GetHttpContent(T value) public string Serialize(T value) { - return System.Text.Json.JsonSerializer.Serialize(value, _options); + return System.Text.Json.JsonSerializer.Serialize(value, (JsonTypeInfo)_options.GetTypeInfo(typeof(T))); } public byte[] SerializeToUtf8Bytes(T value) { - return System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(value, _options); + return System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(value, (JsonTypeInfo)_options.GetTypeInfo(typeof(T))); } public T Deserialize(byte[] json) { - return System.Text.Json.JsonSerializer.Deserialize(json, _options)!; + return System.Text.Json.JsonSerializer.Deserialize(json, (JsonTypeInfo)_options.GetTypeInfo(typeof(T)))!; } public Task DeserializeAsync(HttpContent content, CancellationToken cancellationToken) { - return content.ReadFromJsonAsync(_options, cancellationToken)!; + return content.ReadFromJsonAsync((JsonTypeInfo)_options.GetTypeInfo(typeof(T)), cancellationToken)!; } public async IAsyncEnumerable DeserializeAsync(Stream stream, [EnumeratorCancellation] CancellationToken cancellationToken) @@ -69,7 +76,7 @@ public async IAsyncEnumerable DeserializeAsync(Stream stream, [EnumeratorC while (!buffer.IsEmpty && TryParseJson(ref buffer, out var jsonDocument)) { - yield return jsonDocument!.Deserialize(_options)!; + yield return jsonDocument!.Deserialize((JsonTypeInfo)_options.GetTypeInfo(typeof(T)))!; } if (result.IsCompleted) @@ -95,4 +102,12 @@ private static bool TryParseJson(ref ReadOnlySequence buffer, out JsonDocu return false; } -} \ No newline at end of file +} + +// extended types that are not generated by source generator +[JsonSerializable(typeof(Dictionary[]))] +[JsonSerializable(typeof(ImagesListResponse[]))] +[JsonSerializable(typeof(ContainerListResponse[]))] +[JsonSerializable(typeof(SwarmService[]))] +[JsonSerializable(typeof(IList))] +internal sealed partial class DockerExtendedJsonSerializerContext : JsonSerializerContext { } diff --git a/src/Docker.DotNet/Models/DockerModelsJsonSerializerContext.Generated.cs b/src/Docker.DotNet/Models/DockerModelsJsonSerializerContext.Generated.cs new file mode 100644 index 00000000..4f316fc5 --- /dev/null +++ b/src/Docker.DotNet/Models/DockerModelsJsonSerializerContext.Generated.cs @@ -0,0 +1,300 @@ +namespace Docker.DotNet.Models +{ + [JsonSerializable(typeof(Actor))] + [JsonSerializable(typeof(Annotations))] + [JsonSerializable(typeof(AppArmorOpts))] + [JsonSerializable(typeof(AttestationProperties))] + [JsonSerializable(typeof(AuthConfig))] + [JsonSerializable(typeof(AuthResponse))] + [JsonSerializable(typeof(BindOptions))] + [JsonSerializable(typeof(BlkioStatEntry))] + [JsonSerializable(typeof(BlkioStats))] + [JsonSerializable(typeof(CAConfig))] + [JsonSerializable(typeof(CPUStats))] + [JsonSerializable(typeof(CPUUsage))] + [JsonSerializable(typeof(CapacityRange))] + [JsonSerializable(typeof(ClusterInfo))] + [JsonSerializable(typeof(ClusterOptions))] + [JsonSerializable(typeof(ClusterVolume))] + [JsonSerializable(typeof(ClusterVolumeSpec))] + [JsonSerializable(typeof(Commit))] + [JsonSerializable(typeof(CommitContainerChangesParameters))] + [JsonSerializable(typeof(CommitContainerChangesResponse))] + [JsonSerializable(typeof(ComponentVersion))] + [JsonSerializable(typeof(ConfigReference))] + [JsonSerializable(typeof(ConfigReferenceFileTarget))] + [JsonSerializable(typeof(ConfigReferenceRuntimeTarget))] + [JsonSerializable(typeof(ConsoleSize))] + [JsonSerializable(typeof(ContainerAttachParameters))] + [JsonSerializable(typeof(ContainerConfig))] + [JsonSerializable(typeof(ContainerEventsParameters))] + [JsonSerializable(typeof(ContainerExecCreateParameters))] + [JsonSerializable(typeof(ContainerExecCreateResponse))] + [JsonSerializable(typeof(ContainerExecInspectResponse))] + [JsonSerializable(typeof(ContainerExecStartParameters))] + [JsonSerializable(typeof(ContainerFileSystemChangeResponse))] + [JsonSerializable(typeof(ContainerInspectParameters))] + [JsonSerializable(typeof(ContainerInspectResponse))] + [JsonSerializable(typeof(ContainerKillParameters))] + [JsonSerializable(typeof(ContainerListProcessesParameters))] + [JsonSerializable(typeof(ContainerListResponse))] + [JsonSerializable(typeof(ContainerLogsParameters))] + [JsonSerializable(typeof(ContainerPathStatParameters))] + [JsonSerializable(typeof(ContainerPathStatResponse))] + [JsonSerializable(typeof(ContainerProcessesResponse))] + [JsonSerializable(typeof(ContainerRemoveParameters))] + [JsonSerializable(typeof(ContainerRenameParameters))] + [JsonSerializable(typeof(ContainerResizeParameters))] + [JsonSerializable(typeof(ContainerRestartParameters))] + [JsonSerializable(typeof(ContainerSpec))] + [JsonSerializable(typeof(ContainerStartParameters))] + [JsonSerializable(typeof(ContainerStatsParameters))] + [JsonSerializable(typeof(ContainerStatsResponse))] + [JsonSerializable(typeof(ContainerStatus))] + [JsonSerializable(typeof(ContainerStopParameters))] + [JsonSerializable(typeof(ContainerUpdateParameters))] + [JsonSerializable(typeof(ContainerUpdateResponse))] + [JsonSerializable(typeof(ContainerWaitResponse))] + [JsonSerializable(typeof(ContainerdInfo))] + [JsonSerializable(typeof(ContainerdNamespaces))] + [JsonSerializable(typeof(ContainersListParameters))] + [JsonSerializable(typeof(ContainersPruneParameters))] + [JsonSerializable(typeof(ContainersPruneResponse))] + [JsonSerializable(typeof(CopyToContainerParameters))] + [JsonSerializable(typeof(CreateContainerParameters))] + [JsonSerializable(typeof(CreateContainerResponse))] + [JsonSerializable(typeof(CredentialSpec))] + [JsonSerializable(typeof(DNSConfig))] + [JsonSerializable(typeof(Descriptor))] + [JsonSerializable(typeof(DeviceInfo))] + [JsonSerializable(typeof(DeviceMapping))] + [JsonSerializable(typeof(DeviceRequest))] + [JsonSerializable(typeof(DiscreteGenericResource))] + [JsonSerializable(typeof(DispatcherConfig))] + [JsonSerializable(typeof(DockerOCIImageConfig))] + [JsonSerializable(typeof(DockerOCIImageConfigExt))] + [JsonSerializable(typeof(Driver))] + [JsonSerializable(typeof(DriverData))] + [JsonSerializable(typeof(EncryptionConfig))] + [JsonSerializable(typeof(Endpoint))] + [JsonSerializable(typeof(EndpointIPAMConfig))] + [JsonSerializable(typeof(EndpointResource))] + [JsonSerializable(typeof(EndpointSettings))] + [JsonSerializable(typeof(EndpointSpec))] + [JsonSerializable(typeof(EndpointVirtualIP))] + [JsonSerializable(typeof(EngineDescription))] + [JsonSerializable(typeof(ExecProcessConfig))] + [JsonSerializable(typeof(ExternalCA))] + [JsonSerializable(typeof(FirewallInfo))] + [JsonSerializable(typeof(GenericResource))] + [JsonSerializable(typeof(GlobalJob))] + [JsonSerializable(typeof(GlobalService))] + [JsonSerializable(typeof(Health))] + [JsonSerializable(typeof(HealthSummary))] + [JsonSerializable(typeof(HealthcheckConfig))] + [JsonSerializable(typeof(HealthcheckResult))] + [JsonSerializable(typeof(HostConfig))] + [JsonSerializable(typeof(IPAM))] + [JsonSerializable(typeof(IPAMConfig))] + [JsonSerializable(typeof(IPAMOptions))] + [JsonSerializable(typeof(IPAMStatus))] + [JsonSerializable(typeof(ImageBuildParameters))] + [JsonSerializable(typeof(ImageBuildResult))] + [JsonSerializable(typeof(ImageConfig))] + [JsonSerializable(typeof(ImageDeleteParameters))] + [JsonSerializable(typeof(ImageDeleteResponse))] + [JsonSerializable(typeof(ImageHistoryResponse))] + [JsonSerializable(typeof(ImageInspectResponse))] + [JsonSerializable(typeof(ImageLoadParameters))] + [JsonSerializable(typeof(ImageOptions))] + [JsonSerializable(typeof(ImageProperties))] + [JsonSerializable(typeof(ImagePropertiesSize))] + [JsonSerializable(typeof(ImagePushParameters))] + [JsonSerializable(typeof(ImageSearchResponse))] + [JsonSerializable(typeof(ImageTagParameters))] + [JsonSerializable(typeof(ImagesCreateParameters))] + [JsonSerializable(typeof(ImagesListParameters))] + [JsonSerializable(typeof(ImagesListResponse))] + [JsonSerializable(typeof(ImagesLoadResponse))] + [JsonSerializable(typeof(ImagesPruneParameters))] + [JsonSerializable(typeof(ImagesPruneResponse))] + [JsonSerializable(typeof(ImagesSearchParameters))] + [JsonSerializable(typeof(IndexInfo))] + [JsonSerializable(typeof(Info))] + [JsonSerializable(typeof(JSONError))] + [JsonSerializable(typeof(JSONMessage))] + [JsonSerializable(typeof(JSONProgress))] + [JsonSerializable(typeof(JobStatus))] + [JsonSerializable(typeof(JoinTokens))] + [JsonSerializable(typeof(LogConfig))] + [JsonSerializable(typeof(ManagerStatus))] + [JsonSerializable(typeof(ManifestSummary))] + [JsonSerializable(typeof(ManifestSummarySize))] + [JsonSerializable(typeof(MemoryStats))] + [JsonSerializable(typeof(Message))] + [JsonSerializable(typeof(Meta))] + [JsonSerializable(typeof(Metadata))] + [JsonSerializable(typeof(Mount))] + [JsonSerializable(typeof(MountPoint))] + [JsonSerializable(typeof(NamedGenericResource))] + [JsonSerializable(typeof(Network))] + [JsonSerializable(typeof(NetworkAddressPool))] + [JsonSerializable(typeof(NetworkAttachment))] + [JsonSerializable(typeof(NetworkAttachmentConfig))] + [JsonSerializable(typeof(NetworkAttachmentSpec))] + [JsonSerializable(typeof(NetworkConnectParameters))] + [JsonSerializable(typeof(NetworkDisconnectParameters))] + [JsonSerializable(typeof(NetworkResponse))] + [JsonSerializable(typeof(NetworkSettings))] + [JsonSerializable(typeof(NetworkSettingsSummary))] + [JsonSerializable(typeof(NetworkSpec))] + [JsonSerializable(typeof(NetworkStats))] + [JsonSerializable(typeof(NetworkTask))] + [JsonSerializable(typeof(NetworkingConfig))] + [JsonSerializable(typeof(NetworksCreateParameters))] + [JsonSerializable(typeof(NetworksCreateResponse))] + [JsonSerializable(typeof(NetworksDeleteUnusedParameters))] + [JsonSerializable(typeof(NetworksListParameters))] + [JsonSerializable(typeof(NetworksPruneResponse))] + [JsonSerializable(typeof(NodeCSIInfo))] + [JsonSerializable(typeof(NodeDescription))] + [JsonSerializable(typeof(NodeListResponse))] + [JsonSerializable(typeof(NodeRemoveParameters))] + [JsonSerializable(typeof(NodeStatus))] + [JsonSerializable(typeof(NodeUpdateParameters))] + [JsonSerializable(typeof(OrchestrationConfig))] + [JsonSerializable(typeof(Peer))] + [JsonSerializable(typeof(PeerInfo))] + [JsonSerializable(typeof(PidsStats))] + [JsonSerializable(typeof(Placement))] + [JsonSerializable(typeof(PlacementPreference))] + [JsonSerializable(typeof(Platform))] + [JsonSerializable(typeof(PlatformInfo))] + [JsonSerializable(typeof(Plugin))] + [JsonSerializable(typeof(PluginArgs))] + [JsonSerializable(typeof(PluginCapabilityID))] + [JsonSerializable(typeof(PluginConfig))] + [JsonSerializable(typeof(PluginConfigureParameters))] + [JsonSerializable(typeof(PluginCreateParameters))] + [JsonSerializable(typeof(PluginDescription))] + [JsonSerializable(typeof(PluginDevice))] + [JsonSerializable(typeof(PluginDisableParameters))] + [JsonSerializable(typeof(PluginEnableParameters))] + [JsonSerializable(typeof(PluginEnv))] + [JsonSerializable(typeof(PluginGetPrivilegeParameters))] + [JsonSerializable(typeof(PluginInstallParameters))] + [JsonSerializable(typeof(PluginInterface))] + [JsonSerializable(typeof(PluginLinuxConfig))] + [JsonSerializable(typeof(PluginListParameters))] + [JsonSerializable(typeof(PluginMount))] + [JsonSerializable(typeof(PluginNetworkConfig))] + [JsonSerializable(typeof(PluginPrivilege))] + [JsonSerializable(typeof(PluginRemoveParameters))] + [JsonSerializable(typeof(PluginRootFS))] + [JsonSerializable(typeof(PluginSettings))] + [JsonSerializable(typeof(PluginUpgradeParameters))] + [JsonSerializable(typeof(PluginUser))] + [JsonSerializable(typeof(PluginsInfo))] + [JsonSerializable(typeof(PortBinding))] + [JsonSerializable(typeof(PortConfig))] + [JsonSerializable(typeof(PortStatus))] + [JsonSerializable(typeof(PortSummary))] + [JsonSerializable(typeof(Privileges))] + [JsonSerializable(typeof(PublishStatus))] + [JsonSerializable(typeof(RaftConfig))] + [JsonSerializable(typeof(ReplicatedJob))] + [JsonSerializable(typeof(ReplicatedService))] + [JsonSerializable(typeof(ResourceRequirements))] + [JsonSerializable(typeof(Resources))] + [JsonSerializable(typeof(RestartPolicy))] + [JsonSerializable(typeof(RootFS))] + [JsonSerializable(typeof(RootFSStorage))] + [JsonSerializable(typeof(RootFSStorageSnapshot))] + [JsonSerializable(typeof(Runtime))] + [JsonSerializable(typeof(RuntimePrivilege))] + [JsonSerializable(typeof(RuntimeWithStatus))] + [JsonSerializable(typeof(SELinuxContext))] + [JsonSerializable(typeof(SeccompOpts))] + [JsonSerializable(typeof(Secret))] + [JsonSerializable(typeof(SecretCreateResponse))] + [JsonSerializable(typeof(SecretReference))] + [JsonSerializable(typeof(SecretReferenceFileTarget))] + [JsonSerializable(typeof(ServiceConfig))] + [JsonSerializable(typeof(ServiceCreateParameters))] + [JsonSerializable(typeof(ServiceCreateResponse))] + [JsonSerializable(typeof(ServiceInfo))] + [JsonSerializable(typeof(ServiceListParameters))] + [JsonSerializable(typeof(ServiceLogsParameters))] + [JsonSerializable(typeof(ServiceMode))] + [JsonSerializable(typeof(ServiceSpec))] + [JsonSerializable(typeof(ServiceStatus))] + [JsonSerializable(typeof(ServiceUpdateParameters))] + [JsonSerializable(typeof(ServiceUpdateResponse))] + [JsonSerializable(typeof(Spec))] + [JsonSerializable(typeof(SpreadOver))] + [JsonSerializable(typeof(State))] + [JsonSerializable(typeof(Status))] + [JsonSerializable(typeof(Storage))] + [JsonSerializable(typeof(StorageStats))] + [JsonSerializable(typeof(SubnetStatus))] + [JsonSerializable(typeof(SummaryHostConfig))] + [JsonSerializable(typeof(SwarmConfig))] + [JsonSerializable(typeof(SwarmConfigReference))] + [JsonSerializable(typeof(SwarmConfigSpec))] + [JsonSerializable(typeof(SwarmCreateConfigParameters))] + [JsonSerializable(typeof(SwarmCreateConfigResponse))] + [JsonSerializable(typeof(SwarmDriver))] + [JsonSerializable(typeof(SwarmIPAMConfig))] + [JsonSerializable(typeof(SwarmInitParameters))] + [JsonSerializable(typeof(SwarmInspectResponse))] + [JsonSerializable(typeof(SwarmJoinParameters))] + [JsonSerializable(typeof(SwarmLeaveParameters))] + [JsonSerializable(typeof(SwarmLimit))] + [JsonSerializable(typeof(SwarmNetwork))] + [JsonSerializable(typeof(SwarmPlatform))] + [JsonSerializable(typeof(SwarmResources))] + [JsonSerializable(typeof(SwarmRestartPolicy))] + [JsonSerializable(typeof(SwarmRuntimeSpec))] + [JsonSerializable(typeof(SwarmSecretSpec))] + [JsonSerializable(typeof(SwarmService))] + [JsonSerializable(typeof(SwarmUnlockParameters))] + [JsonSerializable(typeof(SwarmUnlockResponse))] + [JsonSerializable(typeof(SwarmUpdateConfig))] + [JsonSerializable(typeof(SwarmUpdateConfigParameters))] + [JsonSerializable(typeof(SwarmUpdateParameters))] + [JsonSerializable(typeof(SystemInfoResponse))] + [JsonSerializable(typeof(TLSInfo))] + [JsonSerializable(typeof(TaskDefaults))] + [JsonSerializable(typeof(TaskResponse))] + [JsonSerializable(typeof(TaskSpec))] + [JsonSerializable(typeof(TaskStatus))] + [JsonSerializable(typeof(TasksListParameters))] + [JsonSerializable(typeof(ThrottleDevice))] + [JsonSerializable(typeof(ThrottlingData))] + [JsonSerializable(typeof(TmpfsOptions))] + [JsonSerializable(typeof(Topology))] + [JsonSerializable(typeof(TopologyRequirement))] + [JsonSerializable(typeof(TypeBlock))] + [JsonSerializable(typeof(TypeMount))] + [JsonSerializable(typeof(Ulimit))] + [JsonSerializable(typeof(UpdateConfig))] + [JsonSerializable(typeof(UpdateStatus))] + [JsonSerializable(typeof(UsageData))] + [JsonSerializable(typeof(Version))] + [JsonSerializable(typeof(VersionResponse))] + [JsonSerializable(typeof(VolumeAccessMode))] + [JsonSerializable(typeof(VolumeAttachment))] + [JsonSerializable(typeof(VolumeInfo))] + [JsonSerializable(typeof(VolumeOptions))] + [JsonSerializable(typeof(VolumeResponse))] + [JsonSerializable(typeof(VolumeSecret))] + [JsonSerializable(typeof(VolumeTopology))] + [JsonSerializable(typeof(VolumesCreateParameters))] + [JsonSerializable(typeof(VolumesListParameters))] + [JsonSerializable(typeof(VolumesListResponse))] + [JsonSerializable(typeof(VolumesPruneParameters))] + [JsonSerializable(typeof(VolumesPruneResponse))] + [JsonSerializable(typeof(WaitExitError))] + [JsonSerializable(typeof(WeightDevice))] + internal sealed partial class DockerModelsJsonSerializerContext : JsonSerializerContext { } +} diff --git a/src/Docker.DotNet/QueryString.cs b/src/Docker.DotNet/QueryString.cs index e12869e5..89f68134 100644 --- a/src/Docker.DotNet/QueryString.cs +++ b/src/Docker.DotNet/QueryString.cs @@ -1,6 +1,10 @@ namespace Docker.DotNet; -internal class QueryString : IQueryString where T : class +internal class QueryString< +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] +#endif +T> : IQueryString where T : class { private T Object { get; } @@ -86,7 +90,11 @@ private static string[] ConvertValue(IQueryStringConverter converter, object val return converter.Convert(value); } - private static Dictionary FindAttributedPublicProperties() where TAttribType : Attribute + private static Dictionary FindAttributedPublicProperties< +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] +#endif + TValue, TAttribType>() where TAttribType : Attribute { Dictionary? attributedPublicProperties = null; @@ -113,6 +121,9 @@ private static Dictionary FindAttributedPublicPropert return attributedPublicProperties; } +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2072", Justification = "Activator.CreateInstance is only used for value types here; safe for runtime usage.")] +#endif private static bool IsDefaultOfType(object? o) { if (o is ValueType) diff --git a/src/Docker.DotNet/QueryStringConverterInstanceFactory.cs b/src/Docker.DotNet/QueryStringConverterInstanceFactory.cs index 1450f50e..dba24b4e 100644 --- a/src/Docker.DotNet/QueryStringConverterInstanceFactory.cs +++ b/src/Docker.DotNet/QueryStringConverterInstanceFactory.cs @@ -4,20 +4,28 @@ internal class QueryStringConverterInstanceFactory : IQueryStringConverterInstan { private static readonly ConcurrentDictionary ConverterInstanceRegistry = new ConcurrentDictionary(); - public IQueryStringConverter GetConverterInstance(Type t) + public IQueryStringConverter GetConverterInstance( +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] +#endif + Type t) { - return ConverterInstanceRegistry.GetOrAdd( - t, - InitializeConverter); +#pragma warning disable IL2111 // Method with parameters or return value with `DynamicallyAccessedMembersAttribute` is accessed via reflection. Trimmer can't guarantee availability of the requirements of the method. + return ConverterInstanceRegistry.GetOrAdd(t, InitializeConverter); +#pragma warning restore IL2111 // Method with parameters or return value with `DynamicallyAccessedMembersAttribute` is accessed via reflection. Trimmer can't guarantee availability of the requirements of the method. } - private IQueryStringConverter InitializeConverter(Type t) + private static IQueryStringConverter InitializeConverter( +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] +#endif + Type t) { - var instance = Activator.CreateInstance(t) as IQueryStringConverter; - if (instance == null) + if (Activator.CreateInstance(t) is IQueryStringConverter instance) { - throw new InvalidOperationException($"Could not get instance of {t.FullName}"); + return instance; } - return instance; + + throw new InvalidOperationException($"Could not get instance of {t.FullName}"); } } \ No newline at end of file diff --git a/src/Docker.DotNet/QueryStringParameterAttribute.cs b/src/Docker.DotNet/QueryStringParameterAttribute.cs index 6b08d259..e088e39e 100644 --- a/src/Docker.DotNet/QueryStringParameterAttribute.cs +++ b/src/Docker.DotNet/QueryStringParameterAttribute.cs @@ -7,16 +7,23 @@ internal sealed class QueryStringParameterAttribute : Attribute public bool IsRequired { get; private set; } +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] +#endif public Type? ConverterType { get; private set; } - public QueryStringParameterAttribute(string name, bool required, Type? converterType = null) + public QueryStringParameterAttribute(string name, bool required, +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] +#endif + Type? converterType = null) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException(nameof(name)); } - if (converterType != null && !converterType.GetInterfaces().Contains(typeof (IQueryStringConverter))) + if (converterType != null && !converterType.GetInterfaces().Contains(typeof(IQueryStringConverter))) { throw new ArgumentException($"Provided query string converter type is not '{typeof(IQueryStringConverter).FullName}'.", nameof(converterType)); } diff --git a/src/Microsoft.Net.Http.Client/HttpConnection.cs b/src/Microsoft.Net.Http.Client/HttpConnection.cs index fb8c1ab2..7cf88c49 100644 --- a/src/Microsoft.Net.Http.Client/HttpConnection.cs +++ b/src/Microsoft.Net.Http.Client/HttpConnection.cs @@ -26,7 +26,7 @@ await Transport.WriteAsync(requestBytes, 0, requestBytes.Length, cancellationTok { if (request.Content.Headers.ContentLength.HasValue) { -#if NET6_0_OR_GREATER +#if NET await request.Content.CopyToAsync(Transport, cancellationToken) .ConfigureAwait(false); #else @@ -39,7 +39,7 @@ await request.Content.CopyToAsync(Transport) // The length of the data is unknown. Send it in chunked mode. using (var chunkedStream = new ChunkedWriteStream(Transport)) { -#if NET6_0_OR_GREATER +#if NET await request.Content.CopyToAsync(chunkedStream, cancellationToken) .ConfigureAwait(false); #else diff --git a/src/Microsoft.Net.Http.Client/HttpConnectionResponseContent.cs b/src/Microsoft.Net.Http.Client/HttpConnectionResponseContent.cs index 4fbaefc2..5bed410a 100644 --- a/src/Microsoft.Net.Http.Client/HttpConnectionResponseContent.cs +++ b/src/Microsoft.Net.Http.Client/HttpConnectionResponseContent.cs @@ -45,7 +45,7 @@ protected override Task SerializeToStreamAsync(Stream stream, TransportContext? return _responseStream!.CopyToAsync(stream); } -#if NET6_0_OR_GREATER +#if NET protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken) { return _responseStream!.CopyToAsync(stream, cancellationToken); diff --git a/src/Microsoft.Net.Http.Client/RequestExtensions.cs b/src/Microsoft.Net.Http.Client/RequestExtensions.cs index c7872e17..e2be5054 100644 --- a/src/Microsoft.Net.Http.Client/RequestExtensions.cs +++ b/src/Microsoft.Net.Http.Client/RequestExtensions.cs @@ -84,7 +84,7 @@ public static void SetAddressLineProperty(this HttpRequestMessage request, strin public static T? GetProperty(this HttpRequestMessage request, string key) { -#if NET6_0_OR_GREATER +#if NET return request.Options.TryGetValue(new HttpRequestOptionsKey(key), out var obj) ? obj : default; #else return request.Properties.TryGetValue(key, out var obj) ? (T)obj : default; @@ -93,7 +93,7 @@ public static void SetAddressLineProperty(this HttpRequestMessage request, strin public static void SetProperty(this HttpRequestMessage request, string key, T value) { -#if NET6_0_OR_GREATER +#if NET request.Options.Set(new HttpRequestOptionsKey(key), value); #else request.Properties[key] = value; diff --git a/test/Docker.DotNet.Tests/Docker.DotNet.Tests.csproj b/test/Docker.DotNet.Tests/Docker.DotNet.Tests.csproj index 2d80e0e5..190d1748 100644 --- a/test/Docker.DotNet.Tests/Docker.DotNet.Tests.csproj +++ b/test/Docker.DotNet.Tests/Docker.DotNet.Tests.csproj @@ -1,13 +1,21 @@ - + net8.0;net9.0;net10.0 + linux-arm64 + linux-x64 + osx-arm64 + osx-x64 + win-arm64 + win-x64 + Exe false - false + true + true + true - - - + + @@ -24,7 +32,6 @@ - @@ -40,7 +47,10 @@ - + + + + diff --git a/test/Docker.DotNet.Tests/IConfigOperationsTests.cs b/test/Docker.DotNet.Tests/IConfigOperationsTests.cs index 533d5d25..96bb49ac 100644 --- a/test/Docker.DotNet.Tests/IConfigOperationsTests.cs +++ b/test/Docker.DotNet.Tests/IConfigOperationsTests.cs @@ -15,7 +15,7 @@ public IConfigOperationsTests(TestFixture testFixture, ITestOutputHelper testOut [Fact] public async Task SwarmConfig_CanCreateAndRead() { - var currentConfigs = await _testFixture.DockerClient.Configs.ListConfigsAsync(); + var currentConfigs = await _testFixture.DockerClient.Configs.ListConfigsAsync(TestContext.Current.CancellationToken); _testOutputHelper.WriteLine($"Current Configs: {currentConfigs.Count}"); @@ -31,15 +31,15 @@ public async Task SwarmConfig_CanCreateAndRead() Config = testConfigSpec }; - var createdConfig = await _testFixture.DockerClient.Configs.CreateConfigAsync(configParameters); + var createdConfig = await _testFixture.DockerClient.Configs.CreateConfigAsync(configParameters, TestContext.Current.CancellationToken); Assert.NotNull(createdConfig.ID); _testOutputHelper.WriteLine($"Config created: {createdConfig.ID}"); - var configs = await _testFixture.DockerClient.Configs.ListConfigsAsync(); + var configs = await _testFixture.DockerClient.Configs.ListConfigsAsync(TestContext.Current.CancellationToken); Assert.Contains(configs, c => c.ID == createdConfig.ID); _testOutputHelper.WriteLine($"Current Configs: {configs.Count}"); - var configResponse = await _testFixture.DockerClient.Configs.InspectConfigAsync(createdConfig.ID); + var configResponse = await _testFixture.DockerClient.Configs.InspectConfigAsync(createdConfig.ID, TestContext.Current.CancellationToken); Assert.NotNull(configResponse); @@ -51,8 +51,8 @@ public async Task SwarmConfig_CanCreateAndRead() _testOutputHelper.WriteLine("Config created is the same."); - await _testFixture.DockerClient.Configs.RemoveConfigAsync(createdConfig.ID); + await _testFixture.DockerClient.Configs.RemoveConfigAsync(createdConfig.ID, TestContext.Current.CancellationToken); - await Assert.ThrowsAsync(() => _testFixture.DockerClient.Configs.InspectConfigAsync(createdConfig.ID)); + await Assert.ThrowsAsync(() => _testFixture.DockerClient.Configs.InspectConfigAsync(createdConfig.ID, TestContext.Current.CancellationToken)); } } \ No newline at end of file diff --git a/test/Docker.DotNet.Tests/IContainerOperationsTests.cs b/test/Docker.DotNet.Tests/IContainerOperationsTests.cs index ff3ebab9..c2e36892 100644 --- a/test/Docker.DotNet.Tests/IContainerOperationsTests.cs +++ b/test/Docker.DotNet.Tests/IContainerOperationsTests.cs @@ -94,7 +94,7 @@ await _testFixture.DockerClient.Containers.StartContainerAsync( _testFixture.Cts.Token ); - await Task.Delay(TimeSpan.FromSeconds(5)); + await Task.Delay(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken); await _testFixture.DockerClient.Containers.GetContainerLogsAsync( createContainerResponse.ID, @@ -141,7 +141,7 @@ await _testFixture.DockerClient.Containers.StartContainerAsync( _testFixture.Cts.Token ); - await Task.Delay(TimeSpan.FromSeconds(5)); + await Task.Delay(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken); await _testFixture.DockerClient.Containers.GetContainerLogsAsync( createContainerResponse.ID, @@ -280,7 +280,7 @@ await _testFixture.DockerClient.Containers.StartContainerAsync( containerLogsCts.Token ); - await Task.Delay(TimeSpan.FromSeconds(5)); + await Task.Delay(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken); await _testFixture.DockerClient.Containers.StopContainerAsync( createContainerResponse.ID, @@ -328,7 +328,7 @@ await _testFixture.DockerClient.Containers.GetContainerStatsAsync( tcs.Token ); - await Task.Delay(TimeSpan.FromSeconds(10)); + await Task.Delay(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken); Assert.NotEmpty(containerStatsList); Assert.Single(containerStatsList); @@ -341,9 +341,7 @@ public async Task GetContainerStatsAsync_Tty_False_StreamStats() using var tcs = CancellationTokenSource.CreateLinkedTokenSource(_testFixture.Cts.Token); using (tcs.Token.Register(() => throw new TimeoutException("GetContainerStatsAsync_Tty_False_StreamStats"))) { - var method = MethodBase.GetCurrentMethod(); - - _testOutputHelper.WriteLine($"Running test '{method!.Module}' -> '{method!.Name}'"); + _testOutputHelper.WriteLine($"Running test GetContainerStatsAsync_Tty_False_StreamStats"); var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync( new CreateContainerParameters @@ -421,7 +419,7 @@ await _testFixture.DockerClient.Containers.GetContainerStatsAsync( tcs.Token ); - await Task.Delay(TimeSpan.FromSeconds(10)); + await Task.Delay(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken); Assert.NotEmpty(containerStatsList); Assert.Single(containerStatsList); @@ -475,7 +473,7 @@ await _testFixture.DockerClient.Containers.GetContainerStatsAsync( // This is expected to happen on task cancellation. } - await Task.Delay(TimeSpan.FromSeconds(1)); + await Task.Delay(TimeSpan.FromSeconds(1), TestContext.Current.CancellationToken); _testOutputHelper.WriteLine($"Container stats count: {containerStatsList.Count}"); Assert.NotEmpty(containerStatsList); } @@ -727,10 +725,10 @@ public async Task WriteAsync_OnMultiplexedStream_ForwardsInputToPid1Stdin_Comple containerAttachParameters.Stream = true; // When - var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(createContainerParameters); - _ = await _testFixture.DockerClient.Containers.StartContainerAsync(createContainerResponse.ID, new ContainerStartParameters()); + var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(createContainerParameters, TestContext.Current.CancellationToken); + _ = await _testFixture.DockerClient.Containers.StartContainerAsync(createContainerResponse.ID, new ContainerStartParameters(), TestContext.Current.CancellationToken); - using var stream = await _testFixture.DockerClient.Containers.AttachContainerAsync(createContainerResponse.ID, containerAttachParameters); + using var stream = await _testFixture.DockerClient.Containers.AttachContainerAsync(createContainerResponse.ID, containerAttachParameters, TestContext.Current.CancellationToken); await stream.WriteAsync(linefeedByte, 0, linefeedByte.Length, _testFixture.Cts.Token); @@ -762,12 +760,12 @@ public async Task WriteAsync_OnMultiplexedStream_ForwardsInputToExecStdin_Comple var containerExecStartParameters = new ContainerExecStartParameters(); - var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(createContainerParameters); - _ = await _testFixture.DockerClient.Containers.StartContainerAsync(createContainerResponse.ID, new ContainerStartParameters()); + var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(createContainerParameters, TestContext.Current.CancellationToken); + _ = await _testFixture.DockerClient.Containers.StartContainerAsync(createContainerResponse.ID, new ContainerStartParameters(), TestContext.Current.CancellationToken); // When - var containerExecCreateResponse = await _testFixture.DockerClient.Exec.CreateContainerExecAsync(createContainerResponse.ID, containerExecCreateParameters); - using var stream = await _testFixture.DockerClient.Exec.StartContainerExecAsync(containerExecCreateResponse.ID, containerExecStartParameters); + var containerExecCreateResponse = await _testFixture.DockerClient.Exec.CreateContainerExecAsync(createContainerResponse.ID, containerExecCreateParameters, TestContext.Current.CancellationToken); + using var stream = await _testFixture.DockerClient.Exec.StartContainerExecAsync(containerExecCreateResponse.ID, containerExecStartParameters, TestContext.Current.CancellationToken); await stream.WriteAsync(linefeedByte, 0, linefeedByte.Length, _testFixture.Cts.Token); diff --git a/test/Docker.DotNet.Tests/IImageOperationsTests.cs b/test/Docker.DotNet.Tests/IImageOperationsTests.cs index 50d522f7..9eed7749 100644 --- a/test/Docker.DotNet.Tests/IImageOperationsTests.cs +++ b/test/Docker.DotNet.Tests/IImageOperationsTests.cs @@ -50,12 +50,11 @@ await _testFixture.DockerClient.Images.TagImageAsync( [Fact] public Task CreateImageAsync_ErrorResponse_ThrowsDockerApiException() { - return Assert.ThrowsAsync(() => _testFixture.DockerClient.Images.CreateImageAsync( - new ImagesCreateParameters + return Assert.ThrowsAsync(() => _testFixture.DockerClient.Images.CreateImageAsync(new ImagesCreateParameters { FromImage = "1.2.3.Apparently&this$is+not-a_valid%repository//name", Tag = "ancient-one" - }, null, null)); + }, null, null, TestContext.Current.CancellationToken)); } [Fact] diff --git a/test/Docker.DotNet.Tests/ISwarmOperationsTests.cs b/test/Docker.DotNet.Tests/ISwarmOperationsTests.cs index e620446c..9e86e2ed 100644 --- a/test/Docker.DotNet.Tests/ISwarmOperationsTests.cs +++ b/test/Docker.DotNet.Tests/ISwarmOperationsTests.cs @@ -24,7 +24,7 @@ public async Task GetFilteredServicesByName_Succeeds() Name = serviceName, TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } } } - })).ID; + }, TestContext.Current.CancellationToken)).ID; var secondServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters { @@ -33,7 +33,7 @@ public async Task GetFilteredServicesByName_Succeeds() Name = $"service2-{Guid.NewGuid().ToString().Substring(1, 10)}", TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } } } - })).ID; + }, TestContext.Current.CancellationToken)).ID; var thirdServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters { @@ -42,7 +42,7 @@ public async Task GetFilteredServicesByName_Succeeds() Name = $"service3-{Guid.NewGuid().ToString().Substring(1, 10)}", TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } } } - })).ID; + }, TestContext.Current.CancellationToken)).ID; var services = await _testFixture.DockerClient.Swarm.ListServicesAsync(new ServiceListParameters { @@ -57,9 +57,9 @@ public async Task GetFilteredServicesByName_Succeeds() Assert.Single(services); - await _testFixture.DockerClient.Swarm.RemoveServiceAsync(firstServiceId); - await _testFixture.DockerClient.Swarm.RemoveServiceAsync(secondServiceId); - await _testFixture.DockerClient.Swarm.RemoveServiceAsync(thirdServiceId); + await _testFixture.DockerClient.Swarm.RemoveServiceAsync(firstServiceId, TestContext.Current.CancellationToken); + await _testFixture.DockerClient.Swarm.RemoveServiceAsync(secondServiceId, TestContext.Current.CancellationToken); + await _testFixture.DockerClient.Swarm.RemoveServiceAsync(thirdServiceId, TestContext.Current.CancellationToken); } [Fact] @@ -72,7 +72,7 @@ public async Task GetFilteredServicesById_Succeeds() Name = $"service1-{Guid.NewGuid().ToString().Substring(1, 10)}", TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } } } - })).ID; + }, TestContext.Current.CancellationToken)).ID; var secondServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters { @@ -81,7 +81,7 @@ public async Task GetFilteredServicesById_Succeeds() Name = $"service2-{Guid.NewGuid().ToString().Substring(1, 10)}", TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } } } - })).ID; + }, TestContext.Current.CancellationToken)).ID; var thirdServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters { @@ -90,7 +90,7 @@ public async Task GetFilteredServicesById_Succeeds() Name = $"service3-{Guid.NewGuid().ToString().Substring(1, 10)}", TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } } } - })).ID; + }, TestContext.Current.CancellationToken)).ID; var services = await _testFixture.DockerClient.Swarm.ListServicesAsync(new ServiceListParameters { @@ -105,9 +105,9 @@ public async Task GetFilteredServicesById_Succeeds() Assert.Single(services); - await _testFixture.DockerClient.Swarm.RemoveServiceAsync(firstServiceId); - await _testFixture.DockerClient.Swarm.RemoveServiceAsync(secondServiceId); - await _testFixture.DockerClient.Swarm.RemoveServiceAsync(thirdServiceId); + await _testFixture.DockerClient.Swarm.RemoveServiceAsync(firstServiceId, TestContext.Current.CancellationToken); + await _testFixture.DockerClient.Swarm.RemoveServiceAsync(secondServiceId, TestContext.Current.CancellationToken); + await _testFixture.DockerClient.Swarm.RemoveServiceAsync(thirdServiceId, TestContext.Current.CancellationToken); } [Fact] @@ -122,7 +122,7 @@ public async Task GetServices_Succeeds() Name = $"service1-{Guid.NewGuid().ToString().Substring(1, 10)}", TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } } } - })).ID; + }, TestContext.Current.CancellationToken)).ID; var secondServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters { @@ -131,7 +131,7 @@ public async Task GetServices_Succeeds() Name = $"service2-{Guid.NewGuid().ToString().Substring(1, 10)}", TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } } } - })).ID; + }, TestContext.Current.CancellationToken)).ID; var thirdServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters { @@ -140,15 +140,15 @@ public async Task GetServices_Succeeds() Name = $"service3-{Guid.NewGuid().ToString().Substring(1, 10)}", TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } } } - })).ID; + }, TestContext.Current.CancellationToken)).ID; var services = await _testFixture.DockerClient.Swarm.ListServicesAsync(cancellationToken: CancellationToken.None); Assert.True(services.Count() > initialServiceCount); - await _testFixture.DockerClient.Swarm.RemoveServiceAsync(firstServiceId); - await _testFixture.DockerClient.Swarm.RemoveServiceAsync(secondServiceId); - await _testFixture.DockerClient.Swarm.RemoveServiceAsync(thirdServiceId); + await _testFixture.DockerClient.Swarm.RemoveServiceAsync(firstServiceId, TestContext.Current.CancellationToken); + await _testFixture.DockerClient.Swarm.RemoveServiceAsync(secondServiceId, TestContext.Current.CancellationToken); + await _testFixture.DockerClient.Swarm.RemoveServiceAsync(thirdServiceId, TestContext.Current.CancellationToken); } [Fact] @@ -165,14 +165,14 @@ public async Task GetServiceLogs_Succeeds() Name = serviceName, TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID, Command = CommonCommands.EchoToStdoutAndStderr } } } - })).ID; + }, TestContext.Current.CancellationToken)).ID; using var stream = await _testFixture.DockerClient.Swarm.GetServiceLogsAsync(serviceName, false, new ServiceLogsParameters { Follow = true, ShowStdout = true, ShowStderr = true - }); + }, TestContext.Current.CancellationToken); var maxRetries = 3; var currentRetry = 0; @@ -238,7 +238,7 @@ public async Task GetServiceLogs_Succeeds() currentRetry++; if (currentRetry < maxRetries) { - await Task.Delay(delayBetweenRetries); + await Task.Delay(delayBetweenRetries, TestContext.Current.CancellationToken); } } } @@ -246,6 +246,6 @@ public async Task GetServiceLogs_Succeeds() Assert.NotNull(logLines); Assert.NotEmpty(logLines); - await _testFixture.DockerClient.Swarm.RemoveServiceAsync(serviceId); + await _testFixture.DockerClient.Swarm.RemoveServiceAsync(serviceId, TestContext.Current.CancellationToken); } } \ No newline at end of file diff --git a/test/Docker.DotNet.Tests/ISystemOperations.Tests.cs b/test/Docker.DotNet.Tests/ISystemOperations.Tests.cs index b94faa24..d1064779 100644 --- a/test/Docker.DotNet.Tests/ISystemOperations.Tests.cs +++ b/test/Docker.DotNet.Tests/ISystemOperations.Tests.cs @@ -22,14 +22,14 @@ public void Docker_IsRunning() [Fact] public async Task GetSystemInfoAsync_Succeeds() { - var info = await _testFixture.DockerClient.System.GetSystemInfoAsync(); + var info = await _testFixture.DockerClient.System.GetSystemInfoAsync(TestContext.Current.CancellationToken); Assert.NotNull(info.Architecture); } [Fact] public async Task GetVersionAsync_Succeeds() { - var version = await _testFixture.DockerClient.System.GetVersionAsync(); + var version = await _testFixture.DockerClient.System.GetVersionAsync(TestContext.Current.CancellationToken); Assert.NotNull(version.APIVersion); } @@ -40,7 +40,7 @@ public async Task MonitorEventsAsync_EmptyContainersList_CanBeCancelled() using var cts = new CancellationTokenSource(); await cts.CancelAsync(); - await Task.Delay(1); + await Task.Delay(1, TestContext.Current.CancellationToken); await Assert.ThrowsAsync(() => _testFixture.DockerClient.System.MonitorEventsAsync(new ContainerEventsParameters(), progress, cts.Token)); @@ -49,13 +49,13 @@ public async Task MonitorEventsAsync_EmptyContainersList_CanBeCancelled() [Fact] public async Task MonitorEventsAsync_NullParameters_Throws() { - await Assert.ThrowsAsync(() => _testFixture.DockerClient.System.MonitorEventsAsync(null, null)); + await Assert.ThrowsAsync(() => _testFixture.DockerClient.System.MonitorEventsAsync(null, null, TestContext.Current.CancellationToken)); } [Fact] public async Task MonitorEventsAsync_NullProgress_Throws() { - await Assert.ThrowsAsync(() => _testFixture.DockerClient.System.MonitorEventsAsync(new ContainerEventsParameters(), null)); + await Assert.ThrowsAsync(() => _testFixture.DockerClient.System.MonitorEventsAsync(new ContainerEventsParameters(), null, TestContext.Current.CancellationToken)); } [Fact] @@ -90,7 +90,7 @@ await _testFixture.DockerClient.Images.DeleteImageAsync( _testFixture.Cts.Token); // Give it some time for output operation to complete before cancelling task - await Task.Delay(TimeSpan.FromSeconds(1)); + await Task.Delay(TimeSpan.FromSeconds(1), TestContext.Current.CancellationToken); await cts.CancelAsync(); @@ -223,19 +223,19 @@ await _testFixture.DockerClient.Images.TagImageAsync( }); using var cts = CancellationTokenSource.CreateLinkedTokenSource(_testFixture.Cts.Token); - var task = Task.Run(() => _testFixture.DockerClient.System.MonitorEventsAsync(eventsParams, progress, cts.Token)); + var task = Task.Run(() => _testFixture.DockerClient.System.MonitorEventsAsync(eventsParams, progress, cts.Token), TestContext.Current.CancellationToken); // Wait briefly to ensure the monitoring task is fully established before triggering Docker events. // Ideally, the API would return (or signal) once monitoring is active. - await Task.Delay(TimeSpan.FromSeconds(1)); + await Task.Delay(TimeSpan.FromSeconds(1), TestContext.Current.CancellationToken); - await _testFixture.DockerClient.Images.TagImageAsync($"{_testFixture.Repository}:{_testFixture.Tag}", new ImageTagParameters { RepositoryName = _testFixture.Repository, Tag = newTag }); - await _testFixture.DockerClient.Images.DeleteImageAsync($"{_testFixture.Repository}:{newTag}", new ImageDeleteParameters()); + await _testFixture.DockerClient.Images.TagImageAsync($"{_testFixture.Repository}:{_testFixture.Tag}", new ImageTagParameters { RepositoryName = _testFixture.Repository, Tag = newTag }, TestContext.Current.CancellationToken); + await _testFixture.DockerClient.Images.DeleteImageAsync($"{_testFixture.Repository}:{newTag}", new ImageDeleteParameters(), TestContext.Current.CancellationToken); - var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(new CreateContainerParameters { Image = $"{_testFixture.Repository}:{_testFixture.Tag}", Entrypoint = CommonCommands.SleepInfinity }); + var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(new CreateContainerParameters { Image = $"{_testFixture.Repository}:{_testFixture.Tag}", Entrypoint = CommonCommands.SleepInfinity }, TestContext.Current.CancellationToken); await _testFixture.DockerClient.Containers.RemoveContainerAsync(createContainerResponse.ID, new ContainerRemoveParameters(), cts.Token); - await Task.Delay(TimeSpan.FromSeconds(1)); + await Task.Delay(TimeSpan.FromSeconds(1), TestContext.Current.CancellationToken); await cts.CancelAsync(); await Assert.ThrowsAnyAsync(() => task); @@ -247,6 +247,6 @@ await _testFixture.DockerClient.Images.TagImageAsync( [Fact] public async Task PingAsync_Succeeds() { - await _testFixture.DockerClient.System.PingAsync(); + await _testFixture.DockerClient.System.PingAsync(TestContext.Current.CancellationToken); } } \ No newline at end of file diff --git a/test/Docker.DotNet.Tests/JsonEnumMemberConverterTest.cs b/test/Docker.DotNet.Tests/JsonEnumMemberConverterTest.cs index ad26d82b..7a16d9d3 100644 --- a/test/Docker.DotNet.Tests/JsonEnumMemberConverterTest.cs +++ b/test/Docker.DotNet.Tests/JsonEnumMemberConverterTest.cs @@ -26,7 +26,7 @@ public void JsonSerialization_ShouldSerializeAndDeserializeCorrectly(RestartPoli Assert.Equal(restartPolicyKind, deserializedParameters.HostConfig.RestartPolicy.Name); } - private sealed class RestartPolicyKindTestData : TheoryData + public sealed class RestartPolicyKindTestData : TheoryData { public RestartPolicyKindTestData() { diff --git a/test/Docker.DotNet.Tests/JsonRequestContentTests.cs b/test/Docker.DotNet.Tests/JsonRequestContentTests.cs index 7731f612..ef61bb41 100644 --- a/test/Docker.DotNet.Tests/JsonRequestContentTests.cs +++ b/test/Docker.DotNet.Tests/JsonRequestContentTests.cs @@ -17,10 +17,10 @@ public void Constructor_ThrowsArgumentNullException_WhenSerializerIsNull() [Fact] public async Task GetContent_Succeeds_WhenValueAndSerializerAreValid() { - var content = new JsonRequestContent(new[] { 1 }, JsonSerializer.Instance); + var content = new JsonRequestContent[]>([new Dictionary() { { "key", "value" } }], JsonSerializer.Instance); using var httpContent = content.GetContent(); Assert.Equal("application/json; charset=utf-8", httpContent.Headers.ContentType.ToString()); - var jsonString = await httpContent.ReadAsStringAsync(); - Assert.Equal("[1]", jsonString); + var jsonString = await httpContent.ReadAsStringAsync(TestContext.Current.CancellationToken); + Assert.Equal("""[{"key":"value"}]""", jsonString); } } \ No newline at end of file diff --git a/test/Docker.DotNet.Tests/TestFixture.cs b/test/Docker.DotNet.Tests/TestFixture.cs index 9f3c7280..2c5c5205 100644 --- a/test/Docker.DotNet.Tests/TestFixture.cs +++ b/test/Docker.DotNet.Tests/TestFixture.cs @@ -54,7 +54,7 @@ public TestFixture(IMessageSink messageSink) public ImagesListResponse Image { get; private set; } /// - public async Task InitializeAsync() + public async ValueTask InitializeAsync() { const string repository = "alpine"; @@ -102,7 +102,7 @@ await DockerClient.Images.CreateImageAsync(new ImagesCreateParameters { FromImag } /// - public async Task DisposeAsync() + public async ValueTask DisposeAsync() { if (_hasInitializedSwarm) { diff --git a/test/Docker.DotNet.TestsV2/Docker.DotNet.TestsV2.csproj b/test/Docker.DotNet.TestsV2/Docker.DotNet.TestsV2.csproj index 6f9298b7..388cc7cd 100644 --- a/test/Docker.DotNet.TestsV2/Docker.DotNet.TestsV2.csproj +++ b/test/Docker.DotNet.TestsV2/Docker.DotNet.TestsV2.csproj @@ -1,13 +1,21 @@ - + net8.0;net9.0;net10.0 + linux-arm64 + linux-x64 + osx-arm64 + osx-x64 + win-arm64 + win-x64 + Exe false - false + true + true + true - - - + + @@ -18,7 +26,6 @@ - @@ -29,7 +36,8 @@ - - + + + diff --git a/tools/specgen/specgen.go b/tools/specgen/specgen.go index 0672cda9..5b569e04 100644 --- a/tools/specgen/specgen.go +++ b/tools/specgen/specgen.go @@ -855,6 +855,8 @@ func main() { reflectType(t) } + names := make([]string, 0, len(reflectedTypes)) + for k, v := range reflectedTypes { if _, e := os.Stat(path.Join(sourcePath, v.Name+".Generated.cs")); e == nil { panic(fmt.Sprintf("File: (%s.Generated.cs) already exists. Failed to write key same name for key: (%s) type: (%s).", v.Name, k, v.SourceName)) @@ -877,5 +879,34 @@ func main() { f.Close() os.Rename(f.Name(), path.Join(sourcePath, v.Name+".Generated.cs")) + + names = append(names, v.Name) + } + + slices.Sort(names) + + jscf, err := os.Create(path.Join(sourcePath, "DockerModelsJsonSerializerContext.Generated.cs")) + if err != nil { + panic(err) + } + + defer jscf.Close() + + jscb := bufio.NewWriter(jscf) + + fmt.Fprintln(jscb, "namespace Docker.DotNet.Models") + fmt.Fprintln(jscb, "{") + for _, name := range names { + fmt.Fprintf(jscb, " [JsonSerializable(typeof(%s))]\n", name) } + fmt.Fprintln(jscb, " internal sealed partial class DockerModelsJsonSerializerContext : JsonSerializerContext { }") + fmt.Fprintln(jscb, "}") + + err = jscb.Flush() + if err != nil { + os.Remove(jscf.Name()) + panic(err) + } + + jscf.Close() } From 5911a04c4fcaf022712db793948b297a53b01ead Mon Sep 17 00:00:00 2001 From: campersau Date: Wed, 1 Apr 2026 00:10:33 +0200 Subject: [PATCH 2/6] reduce reflection and simplify querystring converter instance factory by using generic attributes --- .../IQueryStringConverterInstanceFactory.cs | 10 ------ ...mitContainerChangesParameters.Generated.cs | 4 +-- .../ContainerAttachParameters.Generated.cs | 10 +++--- .../ContainerEventsParameters.Generated.cs | 2 +- .../ContainerInspectParameters.Generated.cs | 2 +- .../ContainerLogsParameters.Generated.cs | 8 ++--- .../ContainerRemoveParameters.Generated.cs | 6 ++-- .../ContainerStatsParameters.Generated.cs | 4 +-- .../ContainersListParameters.Generated.cs | 6 ++-- .../ContainersPruneParameters.Generated.cs | 2 +- .../CopyToContainerParameters.Generated.cs | 4 +-- .../Models/ImageBuildParameters.Generated.cs | 20 ++++++------ .../Models/ImageDeleteParameters.Generated.cs | 4 +-- .../Models/ImageLoadParameters.Generated.cs | 2 +- .../ImagesCreateParameters.Generated.cs | 2 +- .../Models/ImagesListParameters.Generated.cs | 10 +++--- .../Models/ImagesPruneParameters.Generated.cs | 2 +- .../ImagesSearchParameters.Generated.cs | 2 +- ...etworksDeleteUnusedParameters.Generated.cs | 2 +- .../NetworksListParameters.Generated.cs | 2 +- .../Models/NodeRemoveParameters.Generated.cs | 2 +- .../PluginDisableParameters.Generated.cs | 2 +- .../Models/PluginListParameters.Generated.cs | 2 +- .../PluginRemoveParameters.Generated.cs | 2 +- .../Models/ServiceListParameters.Generated.cs | 4 +-- .../Models/ServiceLogsParameters.Generated.cs | 10 +++--- .../Models/SwarmLeaveParameters.Generated.cs | 2 +- .../Models/SwarmUpdateParameters.Generated.cs | 6 ++-- .../Models/TasksListParameters.Generated.cs | 2 +- .../Models/VolumesListParameters.Generated.cs | 2 +- .../VolumesPruneParameters.Generated.cs | 2 +- src/Docker.DotNet/QueryString.cs | 18 +++++------ .../QueryStringConverterInstanceFactory.cs | 31 ------------------- .../QueryStringParameterAttribute.cs | 26 ++++++---------- tools/specgen/specgen.go | 22 +++++++------ 35 files changed, 94 insertions(+), 143 deletions(-) delete mode 100644 src/Docker.DotNet/IQueryStringConverterInstanceFactory.cs delete mode 100644 src/Docker.DotNet/QueryStringConverterInstanceFactory.cs diff --git a/src/Docker.DotNet/IQueryStringConverterInstanceFactory.cs b/src/Docker.DotNet/IQueryStringConverterInstanceFactory.cs deleted file mode 100644 index a8770c15..00000000 --- a/src/Docker.DotNet/IQueryStringConverterInstanceFactory.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Docker.DotNet; - -internal interface IQueryStringConverterInstanceFactory -{ - IQueryStringConverter GetConverterInstance( -#if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] -#endif - Type t); -} \ No newline at end of file diff --git a/src/Docker.DotNet/Models/CommitContainerChangesParameters.Generated.cs b/src/Docker.DotNet/Models/CommitContainerChangesParameters.Generated.cs index de1e14dc..1eb3e160 100644 --- a/src/Docker.DotNet/Models/CommitContainerChangesParameters.Generated.cs +++ b/src/Docker.DotNet/Models/CommitContainerChangesParameters.Generated.cs @@ -53,10 +53,10 @@ public CommitContainerChangesParameters(ContainerConfig Config) [QueryStringParameter("author", false)] public string? Author { get; set; } - [QueryStringParameter("changes", false, typeof(EnumerableQueryStringConverter))] + [QueryStringParameter("changes", false)] public IList? Changes { get; set; } - [QueryStringParameter("pause", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("pause", false)] public bool? Pause { get; set; } [JsonPropertyName("Hostname")] diff --git a/src/Docker.DotNet/Models/ContainerAttachParameters.Generated.cs b/src/Docker.DotNet/Models/ContainerAttachParameters.Generated.cs index 536058c3..f277ffba 100644 --- a/src/Docker.DotNet/Models/ContainerAttachParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ContainerAttachParameters.Generated.cs @@ -3,22 +3,22 @@ namespace Docker.DotNet.Models { public class ContainerAttachParameters // (main.ContainerAttachParameters) { - [QueryStringParameter("stream", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("stream", false)] public bool? Stream { get; set; } - [QueryStringParameter("stdin", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("stdin", false)] public bool? Stdin { get; set; } - [QueryStringParameter("stdout", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("stdout", false)] public bool? Stdout { get; set; } - [QueryStringParameter("stderr", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("stderr", false)] public bool? Stderr { get; set; } [QueryStringParameter("detachKeys", false)] public string? DetachKeys { get; set; } - [QueryStringParameter("logs", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("logs", false)] public bool? Logs { get; set; } } } diff --git a/src/Docker.DotNet/Models/ContainerEventsParameters.Generated.cs b/src/Docker.DotNet/Models/ContainerEventsParameters.Generated.cs index 7214bcc7..728f69ab 100644 --- a/src/Docker.DotNet/Models/ContainerEventsParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ContainerEventsParameters.Generated.cs @@ -9,7 +9,7 @@ public class ContainerEventsParameters // (main.ContainerEventsParameters) [QueryStringParameter("until", false)] public string? Until { get; set; } - [QueryStringParameter("filters", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("filters", false)] public IDictionary>? Filters { get; set; } } } diff --git a/src/Docker.DotNet/Models/ContainerInspectParameters.Generated.cs b/src/Docker.DotNet/Models/ContainerInspectParameters.Generated.cs index 9da5c347..35cd3880 100644 --- a/src/Docker.DotNet/Models/ContainerInspectParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ContainerInspectParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class ContainerInspectParameters // (main.ContainerInspectParameters) { - [QueryStringParameter("size", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("size", false)] public bool? IncludeSize { get; set; } } } diff --git a/src/Docker.DotNet/Models/ContainerLogsParameters.Generated.cs b/src/Docker.DotNet/Models/ContainerLogsParameters.Generated.cs index 32c8e832..653b9d82 100644 --- a/src/Docker.DotNet/Models/ContainerLogsParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ContainerLogsParameters.Generated.cs @@ -3,10 +3,10 @@ namespace Docker.DotNet.Models { public class ContainerLogsParameters // (main.ContainerLogsParameters) { - [QueryStringParameter("stdout", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("stdout", false)] public bool? ShowStdout { get; set; } - [QueryStringParameter("stderr", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("stderr", false)] public bool? ShowStderr { get; set; } [QueryStringParameter("since", false)] @@ -15,10 +15,10 @@ public class ContainerLogsParameters // (main.ContainerLogsParameters) [QueryStringParameter("until", false)] public string? Until { get; set; } - [QueryStringParameter("timestamps", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("timestamps", false)] public bool? Timestamps { get; set; } - [QueryStringParameter("follow", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("follow", false)] public bool? Follow { get; set; } [QueryStringParameter("tail", false)] diff --git a/src/Docker.DotNet/Models/ContainerRemoveParameters.Generated.cs b/src/Docker.DotNet/Models/ContainerRemoveParameters.Generated.cs index 861ab467..ce982bcc 100644 --- a/src/Docker.DotNet/Models/ContainerRemoveParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ContainerRemoveParameters.Generated.cs @@ -3,13 +3,13 @@ namespace Docker.DotNet.Models { public class ContainerRemoveParameters // (main.ContainerRemoveParameters) { - [QueryStringParameter("v", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("v", false)] public bool? RemoveVolumes { get; set; } - [QueryStringParameter("link", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("link", false)] public bool? RemoveLinks { get; set; } - [QueryStringParameter("force", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("force", false)] public bool? Force { get; set; } } } diff --git a/src/Docker.DotNet/Models/ContainerStatsParameters.Generated.cs b/src/Docker.DotNet/Models/ContainerStatsParameters.Generated.cs index 8c00b12d..fb01ca98 100644 --- a/src/Docker.DotNet/Models/ContainerStatsParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ContainerStatsParameters.Generated.cs @@ -3,10 +3,10 @@ namespace Docker.DotNet.Models { public class ContainerStatsParameters // (main.ContainerStatsParameters) { - [QueryStringParameter("stream", true, typeof(BoolQueryStringConverter))] + [QueryStringParameter("stream", true)] public bool Stream { get; set; } = true; - [QueryStringParameter("one-shot", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("one-shot", false)] public bool? OneShot { get; set; } } } diff --git a/src/Docker.DotNet/Models/ContainersListParameters.Generated.cs b/src/Docker.DotNet/Models/ContainersListParameters.Generated.cs index f7573786..1781acc9 100644 --- a/src/Docker.DotNet/Models/ContainersListParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ContainersListParameters.Generated.cs @@ -3,16 +3,16 @@ namespace Docker.DotNet.Models { public class ContainersListParameters // (main.ContainersListParameters) { - [QueryStringParameter("all", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("all", false)] public bool? All { get; set; } [QueryStringParameter("limit", false)] public long? Limit { get; set; } - [QueryStringParameter("size", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("size", false)] public bool? Size { get; set; } - [QueryStringParameter("filters", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("filters", false)] public IDictionary>? Filters { get; set; } } } diff --git a/src/Docker.DotNet/Models/ContainersPruneParameters.Generated.cs b/src/Docker.DotNet/Models/ContainersPruneParameters.Generated.cs index e25de825..c619c2b6 100644 --- a/src/Docker.DotNet/Models/ContainersPruneParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ContainersPruneParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class ContainersPruneParameters // (main.ContainersPruneParameters) { - [QueryStringParameter("filters", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("filters", false)] public IDictionary>? Filters { get; set; } } } diff --git a/src/Docker.DotNet/Models/CopyToContainerParameters.Generated.cs b/src/Docker.DotNet/Models/CopyToContainerParameters.Generated.cs index b3e0608c..af17cb52 100644 --- a/src/Docker.DotNet/Models/CopyToContainerParameters.Generated.cs +++ b/src/Docker.DotNet/Models/CopyToContainerParameters.Generated.cs @@ -6,10 +6,10 @@ public class CopyToContainerParameters // (main.CopyToContainerParameters) [QueryStringParameter("path", true)] public string Path { get; set; } = default!; - [QueryStringParameter("noOverwriteDirNonDir", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("noOverwriteDirNonDir", false)] public bool? AllowOverwriteDirWithFile { get; set; } - [QueryStringParameter("copyUIDGID", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("copyUIDGID", false)] public bool? CopyUIDGID { get; set; } } } diff --git a/src/Docker.DotNet/Models/ImageBuildParameters.Generated.cs b/src/Docker.DotNet/Models/ImageBuildParameters.Generated.cs index 65d412bb..55f3fcf6 100644 --- a/src/Docker.DotNet/Models/ImageBuildParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ImageBuildParameters.Generated.cs @@ -3,22 +3,22 @@ namespace Docker.DotNet.Models { public class ImageBuildParameters // (main.ImageBuildParameters) { - [QueryStringParameter("t", false, typeof(EnumerableQueryStringConverter))] + [QueryStringParameter("t", false)] public IList? Tags { get; set; } - [QueryStringParameter("q", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("q", false)] public bool? SuppressOutput { get; set; } [QueryStringParameter("remote", false)] public string? RemoteContext { get; set; } - [QueryStringParameter("nocache", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("nocache", false)] public bool? NoCache { get; set; } - [QueryStringParameter("rm", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("rm", false)] public bool? Remove { get; set; } - [QueryStringParameter("forcerm", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("forcerm", false)] public bool? ForceRemove { get; set; } [QueryStringParameter("pull", false)] @@ -51,19 +51,19 @@ public class ImageBuildParameters // (main.ImageBuildParameters) [QueryStringParameter("dockerfile", false)] public string? Dockerfile { get; set; } - [QueryStringParameter("buildargs", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("buildargs", false)] public IDictionary? BuildArgs { get; set; } - [QueryStringParameter("labels", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("labels", false)] public IDictionary? Labels { get; set; } - [QueryStringParameter("squash", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("squash", false)] public bool? Squash { get; set; } - [QueryStringParameter("cachefrom", false, typeof(EnumerableQueryStringConverter))] + [QueryStringParameter("cachefrom", false)] public IList? CacheFrom { get; set; } - [QueryStringParameter("extrahosts", false, typeof(EnumerableQueryStringConverter))] + [QueryStringParameter("extrahosts", false)] public IList? ExtraHosts { get; set; } [QueryStringParameter("target", false)] diff --git a/src/Docker.DotNet/Models/ImageDeleteParameters.Generated.cs b/src/Docker.DotNet/Models/ImageDeleteParameters.Generated.cs index fae052ba..09f3813c 100644 --- a/src/Docker.DotNet/Models/ImageDeleteParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ImageDeleteParameters.Generated.cs @@ -3,10 +3,10 @@ namespace Docker.DotNet.Models { public class ImageDeleteParameters // (main.ImageDeleteParameters) { - [QueryStringParameter("force", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("force", false)] public bool? Force { get; set; } - [QueryStringParameter("noprune", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("noprune", false)] public bool? NoPrune { get; set; } } } diff --git a/src/Docker.DotNet/Models/ImageLoadParameters.Generated.cs b/src/Docker.DotNet/Models/ImageLoadParameters.Generated.cs index 4b13705a..f0106403 100644 --- a/src/Docker.DotNet/Models/ImageLoadParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ImageLoadParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class ImageLoadParameters // (main.ImageLoadParameters) { - [QueryStringParameter("quiet", true, typeof(BoolQueryStringConverter))] + [QueryStringParameter("quiet", true)] public bool Quiet { get; set; } = default!; } } diff --git a/src/Docker.DotNet/Models/ImagesCreateParameters.Generated.cs b/src/Docker.DotNet/Models/ImagesCreateParameters.Generated.cs index 03931b53..36ccc26d 100644 --- a/src/Docker.DotNet/Models/ImagesCreateParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ImagesCreateParameters.Generated.cs @@ -18,7 +18,7 @@ public class ImagesCreateParameters // (main.ImagesCreateParameters) [QueryStringParameter("message", false)] public string? Message { get; set; } - [QueryStringParameter("changes", false, typeof(EnumerableQueryStringConverter))] + [QueryStringParameter("changes", false)] public IList? Changes { get; set; } [QueryStringParameter("platform", false)] diff --git a/src/Docker.DotNet/Models/ImagesListParameters.Generated.cs b/src/Docker.DotNet/Models/ImagesListParameters.Generated.cs index c1e26dec..2efce66a 100644 --- a/src/Docker.DotNet/Models/ImagesListParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ImagesListParameters.Generated.cs @@ -3,19 +3,19 @@ namespace Docker.DotNet.Models { public class ImagesListParameters // (main.ImagesListParameters) { - [QueryStringParameter("all", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("all", false)] public bool? All { get; set; } - [QueryStringParameter("filters", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("filters", false)] public IDictionary>? Filters { get; set; } - [QueryStringParameter("shared-size", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("shared-size", false)] public bool? SharedSize { get; set; } - [QueryStringParameter("digests", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("digests", false)] public bool? Digests { get; set; } - [QueryStringParameter("manifests", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("manifests", false)] public bool? Manifests { get; set; } } } diff --git a/src/Docker.DotNet/Models/ImagesPruneParameters.Generated.cs b/src/Docker.DotNet/Models/ImagesPruneParameters.Generated.cs index e457204b..7d6968db 100644 --- a/src/Docker.DotNet/Models/ImagesPruneParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ImagesPruneParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class ImagesPruneParameters // (main.ImagesPruneParameters) { - [QueryStringParameter("filters", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("filters", false)] public IDictionary>? Filters { get; set; } } } diff --git a/src/Docker.DotNet/Models/ImagesSearchParameters.Generated.cs b/src/Docker.DotNet/Models/ImagesSearchParameters.Generated.cs index 4c6ff82b..680afda8 100644 --- a/src/Docker.DotNet/Models/ImagesSearchParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ImagesSearchParameters.Generated.cs @@ -9,7 +9,7 @@ public class ImagesSearchParameters // (main.ImagesSearchParameters) [QueryStringParameter("limit", false)] public long? Limit { get; set; } - [QueryStringParameter("filters", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("filters", false)] public IDictionary>? Filters { get; set; } } } diff --git a/src/Docker.DotNet/Models/NetworksDeleteUnusedParameters.Generated.cs b/src/Docker.DotNet/Models/NetworksDeleteUnusedParameters.Generated.cs index 1788f844..6d004995 100644 --- a/src/Docker.DotNet/Models/NetworksDeleteUnusedParameters.Generated.cs +++ b/src/Docker.DotNet/Models/NetworksDeleteUnusedParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class NetworksDeleteUnusedParameters // (main.NetworksDeleteUnusedParameters) { - [QueryStringParameter("filters", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("filters", false)] public IDictionary>? Filters { get; set; } } } diff --git a/src/Docker.DotNet/Models/NetworksListParameters.Generated.cs b/src/Docker.DotNet/Models/NetworksListParameters.Generated.cs index 10e2deb0..748bf325 100644 --- a/src/Docker.DotNet/Models/NetworksListParameters.Generated.cs +++ b/src/Docker.DotNet/Models/NetworksListParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class NetworksListParameters // (main.NetworksListParameters) { - [QueryStringParameter("filters", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("filters", false)] public IDictionary>? Filters { get; set; } } } diff --git a/src/Docker.DotNet/Models/NodeRemoveParameters.Generated.cs b/src/Docker.DotNet/Models/NodeRemoveParameters.Generated.cs index 305699ef..301c7f66 100644 --- a/src/Docker.DotNet/Models/NodeRemoveParameters.Generated.cs +++ b/src/Docker.DotNet/Models/NodeRemoveParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class NodeRemoveParameters // (main.NodeRemoveParameters) { - [QueryStringParameter("force", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("force", false)] public bool? Force { get; set; } } } diff --git a/src/Docker.DotNet/Models/PluginDisableParameters.Generated.cs b/src/Docker.DotNet/Models/PluginDisableParameters.Generated.cs index ba4deaee..53effdbb 100644 --- a/src/Docker.DotNet/Models/PluginDisableParameters.Generated.cs +++ b/src/Docker.DotNet/Models/PluginDisableParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class PluginDisableParameters // (main.PluginDisableParameters) { - [QueryStringParameter("force", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("force", false)] public bool? Force { get; set; } } } diff --git a/src/Docker.DotNet/Models/PluginListParameters.Generated.cs b/src/Docker.DotNet/Models/PluginListParameters.Generated.cs index 89dab338..f67b5704 100644 --- a/src/Docker.DotNet/Models/PluginListParameters.Generated.cs +++ b/src/Docker.DotNet/Models/PluginListParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class PluginListParameters // (main.PluginListParameters) { - [QueryStringParameter("filters", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("filters", false)] public IDictionary>? Filters { get; set; } } } diff --git a/src/Docker.DotNet/Models/PluginRemoveParameters.Generated.cs b/src/Docker.DotNet/Models/PluginRemoveParameters.Generated.cs index 3076aff2..e3c28174 100644 --- a/src/Docker.DotNet/Models/PluginRemoveParameters.Generated.cs +++ b/src/Docker.DotNet/Models/PluginRemoveParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class PluginRemoveParameters // (main.PluginRemoveParameters) { - [QueryStringParameter("force", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("force", false)] public bool? Force { get; set; } } } diff --git a/src/Docker.DotNet/Models/ServiceListParameters.Generated.cs b/src/Docker.DotNet/Models/ServiceListParameters.Generated.cs index 593b0abb..0d26404e 100644 --- a/src/Docker.DotNet/Models/ServiceListParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ServiceListParameters.Generated.cs @@ -3,10 +3,10 @@ namespace Docker.DotNet.Models { public class ServiceListParameters // (main.ServiceListParameters) { - [QueryStringParameter("filters", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("filters", false)] public IDictionary>? Filters { get; set; } - [QueryStringParameter("status", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("status", false)] public bool? Status { get; set; } } } diff --git a/src/Docker.DotNet/Models/ServiceLogsParameters.Generated.cs b/src/Docker.DotNet/Models/ServiceLogsParameters.Generated.cs index 67e9fadc..01a5650b 100644 --- a/src/Docker.DotNet/Models/ServiceLogsParameters.Generated.cs +++ b/src/Docker.DotNet/Models/ServiceLogsParameters.Generated.cs @@ -3,25 +3,25 @@ namespace Docker.DotNet.Models { public class ServiceLogsParameters // (main.ServiceLogsParameters) { - [QueryStringParameter("stdout", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("stdout", false)] public bool? ShowStdout { get; set; } - [QueryStringParameter("stderr", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("stderr", false)] public bool? ShowStderr { get; set; } [QueryStringParameter("since", false)] public string? Since { get; set; } - [QueryStringParameter("timestamps", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("timestamps", false)] public bool? Timestamps { get; set; } - [QueryStringParameter("follow", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("follow", false)] public bool? Follow { get; set; } [QueryStringParameter("tail", false)] public string? Tail { get; set; } - [QueryStringParameter("details", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("details", false)] public bool? Details { get; set; } } } diff --git a/src/Docker.DotNet/Models/SwarmLeaveParameters.Generated.cs b/src/Docker.DotNet/Models/SwarmLeaveParameters.Generated.cs index a3109483..48a40697 100644 --- a/src/Docker.DotNet/Models/SwarmLeaveParameters.Generated.cs +++ b/src/Docker.DotNet/Models/SwarmLeaveParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class SwarmLeaveParameters // (main.SwarmLeaveParameters) { - [QueryStringParameter("force", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("force", false)] public bool? Force { get; set; } } } diff --git a/src/Docker.DotNet/Models/SwarmUpdateParameters.Generated.cs b/src/Docker.DotNet/Models/SwarmUpdateParameters.Generated.cs index 0d1c6b93..03969603 100644 --- a/src/Docker.DotNet/Models/SwarmUpdateParameters.Generated.cs +++ b/src/Docker.DotNet/Models/SwarmUpdateParameters.Generated.cs @@ -9,13 +9,13 @@ public class SwarmUpdateParameters // (main.SwarmUpdateParameters) [QueryStringParameter("version", true)] public long Version { get; set; } = default!; - [QueryStringParameter("rotateworkertoken", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("rotateworkertoken", false)] public bool? RotateWorkerToken { get; set; } - [QueryStringParameter("rotatemanagertoken", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("rotatemanagertoken", false)] public bool? RotateManagerToken { get; set; } - [QueryStringParameter("rotatemanagerunlockkey", false, typeof(BoolQueryStringConverter))] + [QueryStringParameter("rotatemanagerunlockkey", false)] public bool? RotateManagerUnlockKey { get; set; } } } diff --git a/src/Docker.DotNet/Models/TasksListParameters.Generated.cs b/src/Docker.DotNet/Models/TasksListParameters.Generated.cs index d7d781d0..908fc2f0 100644 --- a/src/Docker.DotNet/Models/TasksListParameters.Generated.cs +++ b/src/Docker.DotNet/Models/TasksListParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class TasksListParameters // (main.TasksListParameters) { - [QueryStringParameter("filters", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("filters", false)] public IDictionary>? Filters { get; set; } } } diff --git a/src/Docker.DotNet/Models/VolumesListParameters.Generated.cs b/src/Docker.DotNet/Models/VolumesListParameters.Generated.cs index 49f1c909..34d8ef79 100644 --- a/src/Docker.DotNet/Models/VolumesListParameters.Generated.cs +++ b/src/Docker.DotNet/Models/VolumesListParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class VolumesListParameters // (main.VolumesListParameters) { - [QueryStringParameter("filters", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("filters", false)] public IDictionary>? Filters { get; set; } } } diff --git a/src/Docker.DotNet/Models/VolumesPruneParameters.Generated.cs b/src/Docker.DotNet/Models/VolumesPruneParameters.Generated.cs index 6904b653..44881eb2 100644 --- a/src/Docker.DotNet/Models/VolumesPruneParameters.Generated.cs +++ b/src/Docker.DotNet/Models/VolumesPruneParameters.Generated.cs @@ -3,7 +3,7 @@ namespace Docker.DotNet.Models { public class VolumesPruneParameters // (main.VolumesPruneParameters) { - [QueryStringParameter("filters", false, typeof(MapQueryStringConverter))] + [QueryStringParameter("filters", false)] public IDictionary>? Filters { get; set; } } } diff --git a/src/Docker.DotNet/QueryString.cs b/src/Docker.DotNet/QueryString.cs index 89f68134..fa61471a 100644 --- a/src/Docker.DotNet/QueryString.cs +++ b/src/Docker.DotNet/QueryString.cs @@ -4,14 +4,12 @@ internal class QueryString< #if NET [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] #endif -T> : IQueryString where T : class +T> : IQueryString where T : class, new() { private T Object { get; } private Dictionary AttributedPublicProperties { get; } - private IQueryStringConverterInstanceFactory QueryStringConverterInstanceFactory { get; } - public QueryString(T value) { if (EqualityComparer.Default.Equals(value)) @@ -20,7 +18,6 @@ public QueryString(T value) } Object = value; - QueryStringConverterInstanceFactory = new QueryStringConverterInstanceFactory(); AttributedPublicProperties = FindAttributedPublicProperties(); } @@ -45,20 +42,19 @@ public IDictionary GetKeyValuePairs() { var keyStr = attribute.Name; string[] valueStr; - if (attribute.ConverterType == null) - { - valueStr = [value!.ToString()!]; - } - else + if (attribute.GetConverter() is IQueryStringConverter converter) { - var converter = QueryStringConverterInstanceFactory.GetConverterInstance(attribute.ConverterType); valueStr = ConvertValue(converter, value!); if (valueStr == null) { - throw new InvalidOperationException($"Got null from value converter '{attribute.ConverterType.FullName}'"); + throw new InvalidOperationException($"Got null from value converter '{converter.GetType().FullName}'"); } } + else + { + valueStr = [value!.ToString()!]; + } queryParameters[keyStr] = valueStr; } diff --git a/src/Docker.DotNet/QueryStringConverterInstanceFactory.cs b/src/Docker.DotNet/QueryStringConverterInstanceFactory.cs deleted file mode 100644 index dba24b4e..00000000 --- a/src/Docker.DotNet/QueryStringConverterInstanceFactory.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Docker.DotNet; - -internal class QueryStringConverterInstanceFactory : IQueryStringConverterInstanceFactory -{ - private static readonly ConcurrentDictionary ConverterInstanceRegistry = new ConcurrentDictionary(); - - public IQueryStringConverter GetConverterInstance( -#if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] -#endif - Type t) - { -#pragma warning disable IL2111 // Method with parameters or return value with `DynamicallyAccessedMembersAttribute` is accessed via reflection. Trimmer can't guarantee availability of the requirements of the method. - return ConverterInstanceRegistry.GetOrAdd(t, InitializeConverter); -#pragma warning restore IL2111 // Method with parameters or return value with `DynamicallyAccessedMembersAttribute` is accessed via reflection. Trimmer can't guarantee availability of the requirements of the method. - } - - private static IQueryStringConverter InitializeConverter( -#if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] -#endif - Type t) - { - if (Activator.CreateInstance(t) is IQueryStringConverter instance) - { - return instance; - } - - throw new InvalidOperationException($"Could not get instance of {t.FullName}"); - } -} \ No newline at end of file diff --git a/src/Docker.DotNet/QueryStringParameterAttribute.cs b/src/Docker.DotNet/QueryStringParameterAttribute.cs index e088e39e..a582c932 100644 --- a/src/Docker.DotNet/QueryStringParameterAttribute.cs +++ b/src/Docker.DotNet/QueryStringParameterAttribute.cs @@ -1,35 +1,29 @@ namespace Docker.DotNet; [AttributeUsage(AttributeTargets.Property)] -internal sealed class QueryStringParameterAttribute : Attribute +internal class QueryStringParameterAttribute : Attribute { public string Name { get; private set; } public bool IsRequired { get; private set; } -#if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] -#endif - public Type? ConverterType { get; private set; } + public virtual IQueryStringConverter? GetConverter() => null; - public QueryStringParameterAttribute(string name, bool required, -#if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] -#endif - Type? converterType = null) + public QueryStringParameterAttribute(string name, bool required) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException(nameof(name)); } - if (converterType != null && !converterType.GetInterfaces().Contains(typeof(IQueryStringConverter))) - { - throw new ArgumentException($"Provided query string converter type is not '{typeof(IQueryStringConverter).FullName}'.", nameof(converterType)); - } - Name = name; IsRequired = required; - ConverterType = converterType; } +} + +internal sealed class QueryStringParameterAttribute(string name, bool required) : QueryStringParameterAttribute(name, required) where TConverter : IQueryStringConverter, new() +{ + private static readonly ConcurrentDictionary ConverterInstanceRegistry = new ConcurrentDictionary(); + + public override IQueryStringConverter GetConverter() => ConverterInstanceRegistry.GetOrAdd(typeof(TConverter), static _ => new TConverter()); } \ No newline at end of file diff --git a/tools/specgen/specgen.go b/tools/specgen/specgen.go index 5b569e04..b2d492c1 100644 --- a/tools/specgen/specgen.go +++ b/tools/specgen/specgen.go @@ -743,7 +743,18 @@ func reflectTypeMembers(t reflect.Type, m *CSModelType) { restTag.Name = strings.ToLower(f.Name) } - a := CSAttribute{Type: CSType{"", "QueryStringParameter"}} + queryStringParameter := "QueryStringParameter" + + switch f.Type.Kind() { + case reflect.Bool: + queryStringParameter += "" + case reflect.Slice, reflect.Array: + queryStringParameter += "" + case reflect.Map: + queryStringParameter += "" + } + + a := CSAttribute{Type: CSType{"", queryStringParameter}} a.Arguments = append( a.Arguments, CSArgument{ @@ -752,15 +763,6 @@ func reflectTypeMembers(t reflect.Type, m *CSModelType) { CSArgument{strconv.FormatBool(restTag.Required), CSInboxTypesMap[reflect.Bool]}) - switch f.Type.Kind() { - case reflect.Bool: - a.Arguments = append(a.Arguments, CSArgument{Value: "typeof(BoolQueryStringConverter)"}) - case reflect.Slice, reflect.Array: - a.Arguments = append(a.Arguments, CSArgument{Value: "typeof(EnumerableQueryStringConverter)"}) - case reflect.Map: - a.Arguments = append(a.Arguments, CSArgument{Value: "typeof(MapQueryStringConverter)"}) - } - csProp.IsOpt = omitEmpty || !restTag.Required csProp.Attributes = append(csProp.Attributes, a) csProp.DefaultValue = restTag.Default From 3446891830ba8d27818ca3e79f83420ca790bcc6 Mon Sep 17 00:00:00 2001 From: campersau Date: Wed, 1 Apr 2026 09:53:20 +0200 Subject: [PATCH 3/6] only generate json serialize attribute if model gets json serialized --- src/Docker.DotNet/JsonSerializer.cs | 1 + ...erModelsJsonSerializerContext.Generated.cs | 47 ------------------- tools/specgen/csharptype.go | 3 +- tools/specgen/specgen.go | 12 +++-- 4 files changed, 11 insertions(+), 52 deletions(-) diff --git a/src/Docker.DotNet/JsonSerializer.cs b/src/Docker.DotNet/JsonSerializer.cs index 34d5a0c3..04a8a837 100644 --- a/src/Docker.DotNet/JsonSerializer.cs +++ b/src/Docker.DotNet/JsonSerializer.cs @@ -106,6 +106,7 @@ private static bool TryParseJson(ref ReadOnlySequence buffer, out JsonDocu // extended types that are not generated by source generator [JsonSerializable(typeof(Dictionary[]))] +[JsonSerializable(typeof(IDictionary>))] [JsonSerializable(typeof(ImagesListResponse[]))] [JsonSerializable(typeof(ContainerListResponse[]))] [JsonSerializable(typeof(SwarmService[]))] diff --git a/src/Docker.DotNet/Models/DockerModelsJsonSerializerContext.Generated.cs b/src/Docker.DotNet/Models/DockerModelsJsonSerializerContext.Generated.cs index 4f316fc5..6ec7dcda 100644 --- a/src/Docker.DotNet/Models/DockerModelsJsonSerializerContext.Generated.cs +++ b/src/Docker.DotNet/Models/DockerModelsJsonSerializerContext.Generated.cs @@ -14,53 +14,32 @@ namespace Docker.DotNet.Models [JsonSerializable(typeof(CPUUsage))] [JsonSerializable(typeof(CapacityRange))] [JsonSerializable(typeof(ClusterInfo))] - [JsonSerializable(typeof(ClusterOptions))] [JsonSerializable(typeof(ClusterVolume))] [JsonSerializable(typeof(ClusterVolumeSpec))] [JsonSerializable(typeof(Commit))] - [JsonSerializable(typeof(CommitContainerChangesParameters))] [JsonSerializable(typeof(CommitContainerChangesResponse))] [JsonSerializable(typeof(ComponentVersion))] [JsonSerializable(typeof(ConfigReference))] [JsonSerializable(typeof(ConfigReferenceFileTarget))] - [JsonSerializable(typeof(ConfigReferenceRuntimeTarget))] [JsonSerializable(typeof(ConsoleSize))] - [JsonSerializable(typeof(ContainerAttachParameters))] [JsonSerializable(typeof(ContainerConfig))] - [JsonSerializable(typeof(ContainerEventsParameters))] [JsonSerializable(typeof(ContainerExecCreateParameters))] [JsonSerializable(typeof(ContainerExecCreateResponse))] [JsonSerializable(typeof(ContainerExecInspectResponse))] [JsonSerializable(typeof(ContainerExecStartParameters))] [JsonSerializable(typeof(ContainerFileSystemChangeResponse))] - [JsonSerializable(typeof(ContainerInspectParameters))] [JsonSerializable(typeof(ContainerInspectResponse))] - [JsonSerializable(typeof(ContainerKillParameters))] - [JsonSerializable(typeof(ContainerListProcessesParameters))] [JsonSerializable(typeof(ContainerListResponse))] - [JsonSerializable(typeof(ContainerLogsParameters))] - [JsonSerializable(typeof(ContainerPathStatParameters))] [JsonSerializable(typeof(ContainerPathStatResponse))] [JsonSerializable(typeof(ContainerProcessesResponse))] - [JsonSerializable(typeof(ContainerRemoveParameters))] - [JsonSerializable(typeof(ContainerRenameParameters))] - [JsonSerializable(typeof(ContainerResizeParameters))] - [JsonSerializable(typeof(ContainerRestartParameters))] [JsonSerializable(typeof(ContainerSpec))] - [JsonSerializable(typeof(ContainerStartParameters))] - [JsonSerializable(typeof(ContainerStatsParameters))] [JsonSerializable(typeof(ContainerStatsResponse))] [JsonSerializable(typeof(ContainerStatus))] - [JsonSerializable(typeof(ContainerStopParameters))] - [JsonSerializable(typeof(ContainerUpdateParameters))] [JsonSerializable(typeof(ContainerUpdateResponse))] [JsonSerializable(typeof(ContainerWaitResponse))] [JsonSerializable(typeof(ContainerdInfo))] [JsonSerializable(typeof(ContainerdNamespaces))] - [JsonSerializable(typeof(ContainersListParameters))] - [JsonSerializable(typeof(ContainersPruneParameters))] [JsonSerializable(typeof(ContainersPruneResponse))] - [JsonSerializable(typeof(CopyToContainerParameters))] [JsonSerializable(typeof(CreateContainerParameters))] [JsonSerializable(typeof(CreateContainerResponse))] [JsonSerializable(typeof(CredentialSpec))] @@ -71,7 +50,6 @@ namespace Docker.DotNet.Models [JsonSerializable(typeof(DeviceRequest))] [JsonSerializable(typeof(DiscreteGenericResource))] [JsonSerializable(typeof(DispatcherConfig))] - [JsonSerializable(typeof(DockerOCIImageConfig))] [JsonSerializable(typeof(DockerOCIImageConfigExt))] [JsonSerializable(typeof(Driver))] [JsonSerializable(typeof(DriverData))] @@ -87,8 +65,6 @@ namespace Docker.DotNet.Models [JsonSerializable(typeof(ExternalCA))] [JsonSerializable(typeof(FirewallInfo))] [JsonSerializable(typeof(GenericResource))] - [JsonSerializable(typeof(GlobalJob))] - [JsonSerializable(typeof(GlobalService))] [JsonSerializable(typeof(Health))] [JsonSerializable(typeof(HealthSummary))] [JsonSerializable(typeof(HealthcheckConfig))] @@ -101,24 +77,17 @@ namespace Docker.DotNet.Models [JsonSerializable(typeof(ImageBuildParameters))] [JsonSerializable(typeof(ImageBuildResult))] [JsonSerializable(typeof(ImageConfig))] - [JsonSerializable(typeof(ImageDeleteParameters))] [JsonSerializable(typeof(ImageDeleteResponse))] [JsonSerializable(typeof(ImageHistoryResponse))] [JsonSerializable(typeof(ImageInspectResponse))] - [JsonSerializable(typeof(ImageLoadParameters))] [JsonSerializable(typeof(ImageOptions))] [JsonSerializable(typeof(ImageProperties))] [JsonSerializable(typeof(ImagePropertiesSize))] [JsonSerializable(typeof(ImagePushParameters))] [JsonSerializable(typeof(ImageSearchResponse))] - [JsonSerializable(typeof(ImageTagParameters))] [JsonSerializable(typeof(ImagesCreateParameters))] - [JsonSerializable(typeof(ImagesListParameters))] [JsonSerializable(typeof(ImagesListResponse))] - [JsonSerializable(typeof(ImagesLoadResponse))] - [JsonSerializable(typeof(ImagesPruneParameters))] [JsonSerializable(typeof(ImagesPruneResponse))] - [JsonSerializable(typeof(ImagesSearchParameters))] [JsonSerializable(typeof(IndexInfo))] [JsonSerializable(typeof(Info))] [JsonSerializable(typeof(JSONError))] @@ -153,13 +122,10 @@ namespace Docker.DotNet.Models [JsonSerializable(typeof(NetworkingConfig))] [JsonSerializable(typeof(NetworksCreateParameters))] [JsonSerializable(typeof(NetworksCreateResponse))] - [JsonSerializable(typeof(NetworksDeleteUnusedParameters))] - [JsonSerializable(typeof(NetworksListParameters))] [JsonSerializable(typeof(NetworksPruneResponse))] [JsonSerializable(typeof(NodeCSIInfo))] [JsonSerializable(typeof(NodeDescription))] [JsonSerializable(typeof(NodeListResponse))] - [JsonSerializable(typeof(NodeRemoveParameters))] [JsonSerializable(typeof(NodeStatus))] [JsonSerializable(typeof(NodeUpdateParameters))] [JsonSerializable(typeof(OrchestrationConfig))] @@ -175,21 +141,15 @@ namespace Docker.DotNet.Models [JsonSerializable(typeof(PluginCapabilityID))] [JsonSerializable(typeof(PluginConfig))] [JsonSerializable(typeof(PluginConfigureParameters))] - [JsonSerializable(typeof(PluginCreateParameters))] [JsonSerializable(typeof(PluginDescription))] [JsonSerializable(typeof(PluginDevice))] - [JsonSerializable(typeof(PluginDisableParameters))] - [JsonSerializable(typeof(PluginEnableParameters))] [JsonSerializable(typeof(PluginEnv))] - [JsonSerializable(typeof(PluginGetPrivilegeParameters))] [JsonSerializable(typeof(PluginInstallParameters))] [JsonSerializable(typeof(PluginInterface))] [JsonSerializable(typeof(PluginLinuxConfig))] - [JsonSerializable(typeof(PluginListParameters))] [JsonSerializable(typeof(PluginMount))] [JsonSerializable(typeof(PluginNetworkConfig))] [JsonSerializable(typeof(PluginPrivilege))] - [JsonSerializable(typeof(PluginRemoveParameters))] [JsonSerializable(typeof(PluginRootFS))] [JsonSerializable(typeof(PluginSettings))] [JsonSerializable(typeof(PluginUpgradeParameters))] @@ -223,8 +183,6 @@ namespace Docker.DotNet.Models [JsonSerializable(typeof(ServiceCreateParameters))] [JsonSerializable(typeof(ServiceCreateResponse))] [JsonSerializable(typeof(ServiceInfo))] - [JsonSerializable(typeof(ServiceListParameters))] - [JsonSerializable(typeof(ServiceLogsParameters))] [JsonSerializable(typeof(ServiceMode))] [JsonSerializable(typeof(ServiceSpec))] [JsonSerializable(typeof(ServiceStatus))] @@ -248,7 +206,6 @@ namespace Docker.DotNet.Models [JsonSerializable(typeof(SwarmInitParameters))] [JsonSerializable(typeof(SwarmInspectResponse))] [JsonSerializable(typeof(SwarmJoinParameters))] - [JsonSerializable(typeof(SwarmLeaveParameters))] [JsonSerializable(typeof(SwarmLimit))] [JsonSerializable(typeof(SwarmNetwork))] [JsonSerializable(typeof(SwarmPlatform))] @@ -268,13 +225,11 @@ namespace Docker.DotNet.Models [JsonSerializable(typeof(TaskResponse))] [JsonSerializable(typeof(TaskSpec))] [JsonSerializable(typeof(TaskStatus))] - [JsonSerializable(typeof(TasksListParameters))] [JsonSerializable(typeof(ThrottleDevice))] [JsonSerializable(typeof(ThrottlingData))] [JsonSerializable(typeof(TmpfsOptions))] [JsonSerializable(typeof(Topology))] [JsonSerializable(typeof(TopologyRequirement))] - [JsonSerializable(typeof(TypeBlock))] [JsonSerializable(typeof(TypeMount))] [JsonSerializable(typeof(Ulimit))] [JsonSerializable(typeof(UpdateConfig))] @@ -290,9 +245,7 @@ namespace Docker.DotNet.Models [JsonSerializable(typeof(VolumeSecret))] [JsonSerializable(typeof(VolumeTopology))] [JsonSerializable(typeof(VolumesCreateParameters))] - [JsonSerializable(typeof(VolumesListParameters))] [JsonSerializable(typeof(VolumesListResponse))] - [JsonSerializable(typeof(VolumesPruneParameters))] [JsonSerializable(typeof(VolumesPruneResponse))] [JsonSerializable(typeof(WaitExitError))] [JsonSerializable(typeof(WeightDevice))] diff --git a/tools/specgen/csharptype.go b/tools/specgen/csharptype.go index e58935c6..485a2d78 100644 --- a/tools/specgen/csharptype.go +++ b/tools/specgen/csharptype.go @@ -190,7 +190,8 @@ type CSModelType struct { // yet. it is possible that given the recursive nature that it not be // completed but as long as this is true we will not attempt to generate the // type more than once. - IsStarted bool + IsStarted bool + HasJsonSerializableProperties bool } // NewModel creates a new model type with valid slices diff --git a/tools/specgen/specgen.go b/tools/specgen/specgen.go index b2d492c1..5fe8ac4c 100644 --- a/tools/specgen/specgen.go +++ b/tools/specgen/specgen.go @@ -771,6 +771,8 @@ func reflectTypeMembers(t reflect.Type, m *CSModelType) { a.Arguments = append(a.Arguments, CSArgument{jsonName, CSInboxTypesMap[reflect.String]}) csProp.IsOpt = omitEmpty || f.Type.Kind() == reflect.Ptr csProp.Attributes = append(csProp.Attributes, a) + + m.HasJsonSerializableProperties = true } if hasTypeCustomizations { @@ -857,7 +859,7 @@ func main() { reflectType(t) } - names := make([]string, 0, len(reflectedTypes)) + jsonSerializableNames := make([]string, 0, len(reflectedTypes)) for k, v := range reflectedTypes { if _, e := os.Stat(path.Join(sourcePath, v.Name+".Generated.cs")); e == nil { @@ -882,10 +884,12 @@ func main() { f.Close() os.Rename(f.Name(), path.Join(sourcePath, v.Name+".Generated.cs")) - names = append(names, v.Name) + if v.HasJsonSerializableProperties { + jsonSerializableNames = append(jsonSerializableNames, v.Name) + } } - slices.Sort(names) + slices.Sort(jsonSerializableNames) jscf, err := os.Create(path.Join(sourcePath, "DockerModelsJsonSerializerContext.Generated.cs")) if err != nil { @@ -898,7 +902,7 @@ func main() { fmt.Fprintln(jscb, "namespace Docker.DotNet.Models") fmt.Fprintln(jscb, "{") - for _, name := range names { + for _, name := range jsonSerializableNames { fmt.Fprintf(jscb, " [JsonSerializable(typeof(%s))]\n", name) } fmt.Fprintln(jscb, " internal sealed partial class DockerModelsJsonSerializerContext : JsonSerializerContext { }") From f1f6671033d49d139acace645c2466544d8c1b9b Mon Sep 17 00:00:00 2001 From: campersau Date: Wed, 1 Apr 2026 10:54:54 +0200 Subject: [PATCH 4/6] add all array types to DockerExtendedJsonSerializerContext also return IList instead of IEnumerable everywhere for consistency --- .../Endpoints/ConfigsOperations.cs | 2 +- .../Endpoints/ISwarmOperations.cs | 4 +-- .../Endpoints/SwarmOperations.cs | 4 +-- src/Docker.DotNet/JsonSerializer.cs | 31 +++++++++++++++++-- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/Docker.DotNet/Endpoints/ConfigsOperations.cs b/src/Docker.DotNet/Endpoints/ConfigsOperations.cs index d3712136..744f1c8f 100644 --- a/src/Docker.DotNet/Endpoints/ConfigsOperations.cs +++ b/src/Docker.DotNet/Endpoints/ConfigsOperations.cs @@ -11,7 +11,7 @@ internal ConfigOperations(DockerClient client) public async Task> ListConfigsAsync(CancellationToken cancellationToken = default) { - return await _client.MakeRequestAsync>(_client.NoErrorHandlers, HttpMethod.Get, "configs", cancellationToken) + return await _client.MakeRequestAsync(_client.NoErrorHandlers, HttpMethod.Get, "configs", cancellationToken) .ConfigureAwait(false); } diff --git a/src/Docker.DotNet/Endpoints/ISwarmOperations.cs b/src/Docker.DotNet/Endpoints/ISwarmOperations.cs index 1648e5ee..e69c6dec 100644 --- a/src/Docker.DotNet/Endpoints/ISwarmOperations.cs +++ b/src/Docker.DotNet/Endpoints/ISwarmOperations.cs @@ -121,7 +121,7 @@ public interface ISwarmOperations /// 500 - Server error. /// 503 - Node is not part of a swarm. /// - Task> ListServicesAsync(ServiceListParameters? parameters = null, CancellationToken cancellationToken = default); + Task> ListServicesAsync(ServiceListParameters? parameters = null, CancellationToken cancellationToken = default); /// /// Update a service. @@ -198,7 +198,7 @@ public interface ISwarmOperations /// 503 - Node is not part of a swarm. /// /// - Task> ListNodesAsync(CancellationToken cancellationToken = default); + Task> ListNodesAsync(CancellationToken cancellationToken = default); /// /// Inspect a node. diff --git a/src/Docker.DotNet/Endpoints/SwarmOperations.cs b/src/Docker.DotNet/Endpoints/SwarmOperations.cs index 6ff57d85..fa9a77cc 100644 --- a/src/Docker.DotNet/Endpoints/SwarmOperations.cs +++ b/src/Docker.DotNet/Endpoints/SwarmOperations.cs @@ -82,7 +82,7 @@ await _client.MakeRequestAsync([NotInSwarmResponseHandler], HttpMethod.Post, "sw .ConfigureAwait(false); } - public async Task> ListServicesAsync(ServiceListParameters? parameters = null, CancellationToken cancellationToken = default) + public async Task> ListServicesAsync(ServiceListParameters? parameters = null, CancellationToken cancellationToken = default) { var queryParameters = parameters == null ? null : new QueryString(parameters); @@ -193,7 +193,7 @@ private static Dictionary RegistryAuthHeaders(AuthConfig? authCo }; } - public async Task> ListNodesAsync(CancellationToken cancellationToken = default) + public async Task> ListNodesAsync(CancellationToken cancellationToken = default) { return await _client.MakeRequestAsync([NotInSwarmResponseHandler], HttpMethod.Get, "nodes", cancellationToken) .ConfigureAwait(false); diff --git a/src/Docker.DotNet/JsonSerializer.cs b/src/Docker.DotNet/JsonSerializer.cs index 04a8a837..bd4e2514 100644 --- a/src/Docker.DotNet/JsonSerializer.cs +++ b/src/Docker.DotNet/JsonSerializer.cs @@ -105,10 +105,35 @@ private static bool TryParseJson(ref ReadOnlySequence buffer, out JsonDocu } // extended types that are not generated by source generator -[JsonSerializable(typeof(Dictionary[]))] +/// Filters [JsonSerializable(typeof(IDictionary>))] -[JsonSerializable(typeof(ImagesListResponse[]))] + +/// +[JsonSerializable(typeof(SwarmConfig[]))] +/// [JsonSerializable(typeof(ContainerListResponse[]))] +/// +[JsonSerializable(typeof(ContainerFileSystemChangeResponse[]))] + +/// +[JsonSerializable(typeof(ImagesListResponse[]))] +/// +[JsonSerializable(typeof(ImageHistoryResponse[]))] +/// +[JsonSerializable(typeof(Dictionary[]))] +/// +[JsonSerializable(typeof(ImageSearchResponse[]))] + +/// +[JsonSerializable(typeof(NetworkResponse[]))] + +/// +[JsonSerializable(typeof(Plugin[]))] +/// +[JsonSerializable(typeof(PluginPrivilege[]))] + +/// [JsonSerializable(typeof(SwarmService[]))] -[JsonSerializable(typeof(IList))] +/// +[JsonSerializable(typeof(NodeListResponse[]))] internal sealed partial class DockerExtendedJsonSerializerContext : JsonSerializerContext { } From f1aff52f80ed9845bb78452f7e8cba937b76a03c Mon Sep 17 00:00:00 2001 From: campersau Date: Wed, 1 Apr 2026 11:11:16 +0200 Subject: [PATCH 5/6] move System.Text.Json.Serialization.Metadata to csproj Using --- src/Docker.DotNet/Docker.DotNet.csproj | 3 ++- src/Docker.DotNet/DockerClient.cs | 2 -- src/Docker.DotNet/JsonSerializer.cs | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Docker.DotNet/Docker.DotNet.csproj b/src/Docker.DotNet/Docker.DotNet.csproj index 5ce06281..1430a200 100644 --- a/src/Docker.DotNet/Docker.DotNet.csproj +++ b/src/Docker.DotNet/Docker.DotNet.csproj @@ -1,4 +1,4 @@ - + Docker.DotNet Docker.DotNet.Enhanced @@ -50,6 +50,7 @@ + diff --git a/src/Docker.DotNet/DockerClient.cs b/src/Docker.DotNet/DockerClient.cs index d9f1b1b5..6328e8f7 100644 --- a/src/Docker.DotNet/DockerClient.cs +++ b/src/Docker.DotNet/DockerClient.cs @@ -1,7 +1,5 @@ namespace Docker.DotNet; -using System; - public sealed class DockerClient : IDockerClient { internal readonly IEnumerable NoErrorHandlers = Enumerable.Empty(); diff --git a/src/Docker.DotNet/JsonSerializer.cs b/src/Docker.DotNet/JsonSerializer.cs index bd4e2514..711a9fff 100644 --- a/src/Docker.DotNet/JsonSerializer.cs +++ b/src/Docker.DotNet/JsonSerializer.cs @@ -1,5 +1,3 @@ -using System.Text.Json.Serialization.Metadata; - namespace Docker.DotNet; internal sealed class JsonSerializer From dd6d702b7e47535f2a9f2714976fa6b42a102329 Mon Sep 17 00:00:00 2001 From: campersau Date: Wed, 1 Apr 2026 12:00:34 +0200 Subject: [PATCH 6/6] remove ConcurrentDictionary and use static field instead for query string converter --- src/Docker.DotNet/QueryStringParameterAttribute.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Docker.DotNet/QueryStringParameterAttribute.cs b/src/Docker.DotNet/QueryStringParameterAttribute.cs index a582c932..754dd4ed 100644 --- a/src/Docker.DotNet/QueryStringParameterAttribute.cs +++ b/src/Docker.DotNet/QueryStringParameterAttribute.cs @@ -23,7 +23,7 @@ public QueryStringParameterAttribute(string name, bool required) internal sealed class QueryStringParameterAttribute(string name, bool required) : QueryStringParameterAttribute(name, required) where TConverter : IQueryStringConverter, new() { - private static readonly ConcurrentDictionary ConverterInstanceRegistry = new ConcurrentDictionary(); + private static readonly TConverter ConverterInstance = new TConverter(); - public override IQueryStringConverter GetConverter() => ConverterInstanceRegistry.GetOrAdd(typeof(TConverter), static _ => new TConverter()); + public override IQueryStringConverter GetConverter() => ConverterInstance; } \ No newline at end of file