You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by aa...@apache.org on 2023/03/16 07:35:20 UTC

[rocketmq-clients] 01/05: Make it compatible with .NET standard 2.1

This is an automated email from the ASF dual-hosted git repository.

aaronai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/rocketmq-clients.git

commit bbc8fc987f0488bf29c00dae5cd5553081183154
Author: Aaron Ai <ya...@gmail.com>
AuthorDate: Wed Mar 15 22:35:42 2023 +0800

    Make it compatible with .NET standard 2.1
---
 csharp/examples/ProducerBenchmark.cs               |  8 ++-
 csharp/examples/ProducerDelayMessageExample.cs     |  2 +-
 csharp/examples/ProducerFifoMessageExample.cs      |  2 +-
 csharp/examples/ProducerNormalMessageExample.cs    |  2 +-
 .../examples/ProducerTransactionMessageExample.cs  |  2 +-
 csharp/examples/SimpleConsumerExample.cs           |  2 +-
 csharp/examples/examples.csproj                    |  2 +-
 csharp/rocketmq-client-csharp/Client.cs            | 10 ++-
 .../ConfigFileCredentialsProvider.cs               | 83 ----------------------
 csharp/rocketmq-client-csharp/Consumer.cs          |  2 +-
 csharp/rocketmq-client-csharp/Endpoints.cs         |  2 +-
 csharp/rocketmq-client-csharp/Message.cs           |  6 +-
 .../rocketmq-client-csharp/MessageIdGenerator.cs   |  2 +-
 csharp/rocketmq-client-csharp/MessageView.cs       |  6 +-
 csharp/rocketmq-client-csharp/MetadataConstants.cs |  2 +-
 csharp/rocketmq-client-csharp/MetricConstant.cs    |  2 +-
 csharp/rocketmq-client-csharp/MqLogManager.cs      | 10 +--
 csharp/rocketmq-client-csharp/Producer.cs          |  7 +-
 .../PublishingLoadBalancer.cs                      |  2 +-
 csharp/rocketmq-client-csharp/RpcClient.cs         | 21 +++---
 csharp/rocketmq-client-csharp/Session.cs           |  2 +-
 csharp/rocketmq-client-csharp/SimpleConsumer.cs    |  2 +-
 .../SubscriptionLoadBalancer.cs                    |  2 +-
 csharp/rocketmq-client-csharp/UserAgent.cs         |  2 +-
 csharp/rocketmq-client-csharp/Utilities.cs         | 37 +++++++++-
 .../rocketmq-client-csharp.csproj                  | 32 ++++-----
 csharp/tests/ConfigFileCredentialsProviderTest.cs  | 59 ---------------
 csharp/tests/UtilitiesTest.cs                      | 50 +++++++++++++
 28 files changed, 152 insertions(+), 209 deletions(-)

diff --git a/csharp/examples/ProducerBenchmark.cs b/csharp/examples/ProducerBenchmark.cs
index 7e2679aa..f7de1c89 100644
--- a/csharp/examples/ProducerBenchmark.cs
+++ b/csharp/examples/ProducerBenchmark.cs
@@ -29,11 +29,13 @@ namespace examples
     {
         private static readonly Logger Logger = MqLogManager.Instance.GetCurrentClassLogger();
 
-        private static readonly SemaphoreSlim Semaphore = new(0);
+        private static readonly SemaphoreSlim Semaphore = new SemaphoreSlim(0);
         private const int TpsLimit = 300;
         private static long _successCounter;
         private static long _failureCounter;
-        private static readonly BlockingCollection<Task<ISendReceipt>> Tasks = new();
+
+        private static readonly BlockingCollection<Task<ISendReceipt>> Tasks =
+            new BlockingCollection<Task<ISendReceipt>>();
 
         private static void DoStats()
         {
@@ -88,7 +90,7 @@ namespace examples
 
             const string topic = "yourNormalTopic";
             // In most case, you don't need to create too many producers, single pattern is recommended.
-            await using var producer = await new Producer.Builder()
+            var producer = await new Producer.Builder()
                 // Set the topic name(s), which is optional but recommended.
                 // It makes producer could prefetch the topic route before message publishing.
                 .SetTopics(topic)
diff --git a/csharp/examples/ProducerDelayMessageExample.cs b/csharp/examples/ProducerDelayMessageExample.cs
index 84808872..5d905408 100644
--- a/csharp/examples/ProducerDelayMessageExample.cs
+++ b/csharp/examples/ProducerDelayMessageExample.cs
@@ -43,7 +43,7 @@ namespace examples
             const string topic = "yourDelayTopic";
             // In most case, you don't need to create too many producers, single pattern is recommended.
             // Producer here will be closed automatically.
-            await using var producer = await new Producer.Builder()
+            var producer = await new Producer.Builder()
                 // Set the topic name(s), which is optional but recommended.
                 // It makes producer could prefetch the topic route before message publishing.
                 .SetTopics(topic)
diff --git a/csharp/examples/ProducerFifoMessageExample.cs b/csharp/examples/ProducerFifoMessageExample.cs
index 6a96fa0a..0ce94ee3 100644
--- a/csharp/examples/ProducerFifoMessageExample.cs
+++ b/csharp/examples/ProducerFifoMessageExample.cs
@@ -43,7 +43,7 @@ namespace examples
             const string topic = "yourFifoTopic";
             // In most case, you don't need to create too many producers, single pattern is recommended.
             // Producer here will be closed automatically.
-            await using var producer = await new Producer.Builder()
+            var producer = await new Producer.Builder()
                 // Set the topic name(s), which is optional but recommended.
                 // It makes producer could prefetch the topic route before message publishing.
                 .SetTopics(topic)
diff --git a/csharp/examples/ProducerNormalMessageExample.cs b/csharp/examples/ProducerNormalMessageExample.cs
index b9b85b73..2598c739 100644
--- a/csharp/examples/ProducerNormalMessageExample.cs
+++ b/csharp/examples/ProducerNormalMessageExample.cs
@@ -42,7 +42,7 @@ namespace examples
             const string topic = "yourNormalTopic";
             // In most case, you don't need to create too many producers, single pattern is recommended.
             // Producer here will be closed automatically.
-            await using var producer = await new Producer.Builder()
+            var producer = await new Producer.Builder()
                 // Set the topic name(s), which is optional but recommended.
                 // It makes producer could prefetch the topic route before message publishing.
                 .SetTopics(topic)
diff --git a/csharp/examples/ProducerTransactionMessageExample.cs b/csharp/examples/ProducerTransactionMessageExample.cs
index 1b5b7aa4..dff74268 100644
--- a/csharp/examples/ProducerTransactionMessageExample.cs
+++ b/csharp/examples/ProducerTransactionMessageExample.cs
@@ -51,7 +51,7 @@ namespace examples
             const string topic = "yourTransactionTopic";
             // In most case, you don't need to create too many producers, single pattern is recommended.
             // Producer here will be closed automatically.
-            await using var producer = await new Producer.Builder()
+            var producer = await new Producer.Builder()
                 // Set the topic name(s), which is optional but recommended.
                 // It makes producer could prefetch the topic route before message publishing.
                 .SetTopics(topic)
diff --git a/csharp/examples/SimpleConsumerExample.cs b/csharp/examples/SimpleConsumerExample.cs
index fd0d9d7a..11dbedb0 100644
--- a/csharp/examples/SimpleConsumerExample.cs
+++ b/csharp/examples/SimpleConsumerExample.cs
@@ -46,7 +46,7 @@ namespace examples
             var subscription = new Dictionary<string, FilterExpression>
                 { { topic, new FilterExpression("*") } };
             // In most case, you don't need to create too many consumers, single pattern is recommended.
-            await using var simpleConsumer = await new SimpleConsumer.Builder()
+            var simpleConsumer = await new SimpleConsumer.Builder()
                 .SetClientConfig(clientConfig)
                 .SetConsumerGroup(consumerGroup)
                 .SetAwaitDuration(TimeSpan.FromSeconds(15))
diff --git a/csharp/examples/examples.csproj b/csharp/examples/examples.csproj
index ebdf0af4..37f6a477 100644
--- a/csharp/examples/examples.csproj
+++ b/csharp/examples/examples.csproj
@@ -5,6 +5,6 @@
 
     <PropertyGroup>
         <OutputType>Exe</OutputType>
-        <TargetFramework>net5.0</TargetFramework>
+        <TargetFrameworks>net5.0;netcoreapp3.1</TargetFrameworks>
     </PropertyGroup>
 </Project>
diff --git a/csharp/rocketmq-client-csharp/Client.cs b/csharp/rocketmq-client-csharp/Client.cs
index ec0ea6a7..d619280b 100644
--- a/csharp/rocketmq-client-csharp/Client.cs
+++ b/csharp/rocketmq-client-csharp/Client.cs
@@ -205,7 +205,7 @@ namespace Org.Apache.Rocketmq
             try
             {
                 Logger.Info($"Start to update topic route cache for a new round, clientId={ClientId}");
-                Dictionary<string, Task<TopicRouteData>> responses = new();
+                Dictionary<string, Task<TopicRouteData>> responses = new Dictionary<string, Task<TopicRouteData>>();
 
                 foreach (var topic in GetTopics())
                 {
@@ -255,9 +255,7 @@ namespace Org.Apache.Rocketmq
             ThreadPool.GetAvailableThreads(out var availableWorker, out var availableIo);
             Logger.Info(
                 $"ClientId={ClientId}, ClientVersion={MetadataConstants.Instance.ClientVersion}, " +
-                $".NET Version={Environment.Version}, ThreadCount={ThreadPool.ThreadCount}, " +
-                $"CompletedWorkItemCount={ThreadPool.CompletedWorkItemCount}, " +
-                $"PendingWorkItemCount={ThreadPool.PendingWorkItemCount}, AvailableWorkerThreads={availableWorker}, " +
+                $".NET Version={Environment.Version}, AvailableWorkerThreads={availableWorker}, " +
                 $"AvailableCompletionPortThreads={availableIo}");
         }
 
@@ -345,8 +343,8 @@ namespace Org.Apache.Rocketmq
             {
                 var endpoints = GetTotalRouteEndpoints();
                 var request = WrapHeartbeatRequest();
-                Dictionary<Endpoints, Task<RpcInvocation<Proto.HeartbeatRequest, Proto.HeartbeatResponse>>>
-                    invocations = new();
+                var invocations =
+                    new Dictionary<Endpoints, Task<RpcInvocation<Proto.HeartbeatRequest, Proto.HeartbeatResponse>>>();
 
                 // Collect task into a map.
                 foreach (var item in endpoints)
diff --git a/csharp/rocketmq-client-csharp/ConfigFileCredentialsProvider.cs b/csharp/rocketmq-client-csharp/ConfigFileCredentialsProvider.cs
deleted file mode 100644
index 93b9e9ef..00000000
--- a/csharp/rocketmq-client-csharp/ConfigFileCredentialsProvider.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System.IO;
-using System;
-using System.Text.Json;
-using System.Collections.Generic;
-using NLog;
-
-namespace Org.Apache.Rocketmq
-{
-    /**
-     * File-based credentials provider that reads JSON configurations from ${HOME}/.rocketmq/config
-     * A sample config content is as follows:
-     * {"AccessKey": "key", "AccessSecret": "secret"}
-     */
-    public class ConfigFileCredentialsProvider
-    {
-        private static readonly Logger Logger = MqLogManager.Instance.GetCurrentClassLogger();
-
-        public ConfigFileCredentialsProvider()
-        {
-            var configFilePath = DefaultConfigFilePath();
-
-            if (!File.Exists(configFilePath))
-            {
-                Logger.Warn("Config file[{}] does not exist", configFilePath);
-                return;
-            }
-
-            try
-            {
-                using var reader = new StreamReader(configFilePath);
-                string json = reader.ReadToEnd();
-                var kv = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
-                if (null == kv)
-                {
-                    Logger.Error($"Failed to parse JSON configuration: {json}");
-                    return;
-                }
-
-                _accessKey = kv["AccessKey"];
-                _accessSecret = kv["AccessSecret"];
-                _valid = true;
-            }
-            catch (IOException e)
-            {
-                Logger.Error($"Failed to read cofig file. Cause: {e.Message}");
-            }
-        }
-
-        public SessionCredentials GetCredentials()
-        {
-            return !_valid ? null : new SessionCredentials(_accessKey, _accessSecret);
-        }
-
-        public static string DefaultConfigFilePath()
-        {
-            var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
-            string[] pathSegments = { home, ".rocketmq", "config" };
-            return string.Join(Path.DirectorySeparatorChar, pathSegments);
-        }
-
-        private readonly string _accessKey;
-        private readonly string _accessSecret;
-
-        private readonly bool _valid;
-    }
-}
\ No newline at end of file
diff --git a/csharp/rocketmq-client-csharp/Consumer.cs b/csharp/rocketmq-client-csharp/Consumer.cs
index 998a3766..0bf7a45a 100644
--- a/csharp/rocketmq-client-csharp/Consumer.cs
+++ b/csharp/rocketmq-client-csharp/Consumer.cs
@@ -27,7 +27,7 @@ namespace Org.Apache.Rocketmq
 {
     public abstract class Consumer : Client
     {
-        internal static readonly Regex ConsumerGroupRegex = new("^[%a-zA-Z0-9_-]+$");
+        internal static readonly Regex ConsumerGroupRegex = new Regex("^[%a-zA-Z0-9_-]+$");
         protected readonly string ConsumerGroup;
 
         protected Consumer(ClientConfig clientConfig, string consumerGroup) : base(
diff --git a/csharp/rocketmq-client-csharp/Endpoints.cs b/csharp/rocketmq-client-csharp/Endpoints.cs
index dbf9bdfb..dc015e2b 100644
--- a/csharp/rocketmq-client-csharp/Endpoints.cs
+++ b/csharp/rocketmq-client-csharp/Endpoints.cs
@@ -28,7 +28,7 @@ namespace Org.Apache.Rocketmq
         private const string HttpsPrefix = "https://";
         private const int DefaultPort = 80;
 
-        private static readonly AddressListEqualityComparer AddressListComparer = new();
+        private static readonly AddressListEqualityComparer AddressListComparer = new AddressListEqualityComparer();
         private const string EndpointSeparator = ":";
         public List<Address> Addresses { get; }
         private AddressScheme Scheme { get; }
diff --git a/csharp/rocketmq-client-csharp/Message.cs b/csharp/rocketmq-client-csharp/Message.cs
index b71a4650..bbd2c20e 100644
--- a/csharp/rocketmq-client-csharp/Message.cs
+++ b/csharp/rocketmq-client-csharp/Message.cs
@@ -24,7 +24,7 @@ namespace Org.Apache.Rocketmq
 {
     public class Message
     {
-        internal static readonly Regex TopicRegex = new("^[%a-zA-Z0-9_-]+$");
+        internal static readonly Regex TopicRegex = new Regex("^[%a-zA-Z0-9_-]+$");
 
         private Message(string topic, byte[] body, string tag, List<string> keys,
             Dictionary<string, string> properties, DateTime? deliveryTimestamp, string messageGroup)
@@ -75,8 +75,8 @@ namespace Org.Apache.Rocketmq
             private string _topic;
             private byte[] _body;
             private string _tag;
-            private List<string> _keys = new();
-            private readonly Dictionary<string, string> _properties = new();
+            private List<string> _keys = new List<string>();
+            private readonly Dictionary<string, string> _properties = new Dictionary<string, string>();
             private DateTime? _deliveryTimestamp;
             private string _messageGroup;
 
diff --git a/csharp/rocketmq-client-csharp/MessageIdGenerator.cs b/csharp/rocketmq-client-csharp/MessageIdGenerator.cs
index 60620ef0..b55b0456 100644
--- a/csharp/rocketmq-client-csharp/MessageIdGenerator.cs
+++ b/csharp/rocketmq-client-csharp/MessageIdGenerator.cs
@@ -28,7 +28,7 @@ namespace Org.Apache.Rocketmq
     public class MessageIdGenerator
     {
         public const string Version = "01";
-        private static readonly MessageIdGenerator Instance = new();
+        private static readonly MessageIdGenerator Instance = new MessageIdGenerator();
 
         private readonly string _prefix;
 
diff --git a/csharp/rocketmq-client-csharp/MessageView.cs b/csharp/rocketmq-client-csharp/MessageView.cs
index 461f7781..63906344 100644
--- a/csharp/rocketmq-client-csharp/MessageView.cs
+++ b/csharp/rocketmq-client-csharp/MessageView.cs
@@ -104,7 +104,7 @@ namespace Org.Apache.Rocketmq
                     }
                 case Proto.DigestType.Md5:
                     {
-                        var expectedCheckSum = Convert.ToHexString(MD5.HashData(raw));
+                        var expectedCheckSum = Utilities.ComputeMd5Hash(raw);
                         if (!expectedCheckSum.Equals(checkSum))
                         {
                             corrupted = true;
@@ -114,7 +114,7 @@ namespace Org.Apache.Rocketmq
                     }
                 case Proto.DigestType.Sha1:
                     {
-                        var expectedCheckSum = Convert.ToHexString(SHA1.HashData(raw));
+                        var expectedCheckSum = Utilities.ComputeSha1Hash(raw);
                         if (!expectedCheckSum.Equals(checkSum))
                         {
                             corrupted = true;
@@ -158,7 +158,7 @@ namespace Org.Apache.Rocketmq
             var messageGroup = systemProperties.HasMessageGroup ? systemProperties.MessageGroup : null;
             DateTime? deliveryTime = null == systemProperties.DeliveryTimestamp
                 ? null
-                : TimeZoneInfo.ConvertTimeFromUtc(systemProperties.DeliveryTimestamp.ToDateTime(), TimeZoneInfo.Local);
+                : (DateTime?)TimeZoneInfo.ConvertTimeFromUtc(systemProperties.DeliveryTimestamp.ToDateTime(), TimeZoneInfo.Local);
             var keys = systemProperties.Keys.ToList();
 
             var bornHost = systemProperties.BornHost;
diff --git a/csharp/rocketmq-client-csharp/MetadataConstants.cs b/csharp/rocketmq-client-csharp/MetadataConstants.cs
index 07907758..1deddcd2 100644
--- a/csharp/rocketmq-client-csharp/MetadataConstants.cs
+++ b/csharp/rocketmq-client-csharp/MetadataConstants.cs
@@ -43,7 +43,7 @@ namespace Org.Apache.Rocketmq
         public string ClientVersion { get; }
 
 
-        public static readonly MetadataConstants Instance = new();
+        public static readonly MetadataConstants Instance = new MetadataConstants();
 
         private MetadataConstants()
         {
diff --git a/csharp/rocketmq-client-csharp/MetricConstant.cs b/csharp/rocketmq-client-csharp/MetricConstant.cs
index e19288de..bfc64b40 100644
--- a/csharp/rocketmq-client-csharp/MetricConstant.cs
+++ b/csharp/rocketmq-client-csharp/MetricConstant.cs
@@ -42,7 +42,7 @@ namespace Org.Apache.Rocketmq
         public readonly ExplicitBucketHistogramConfiguration AwaitTimeBucket;
         public readonly ExplicitBucketHistogramConfiguration ProcessTimeBucket;
 
-        public static readonly MetricConstant Instance = new();
+        public static readonly MetricConstant Instance = new MetricConstant();
 
         private MetricConstant()
         {
diff --git a/csharp/rocketmq-client-csharp/MqLogManager.cs b/csharp/rocketmq-client-csharp/MqLogManager.cs
index 1e67bb56..1e68ea43 100644
--- a/csharp/rocketmq-client-csharp/MqLogManager.cs
+++ b/csharp/rocketmq-client-csharp/MqLogManager.cs
@@ -33,19 +33,19 @@ namespace Org.Apache.Rocketmq
     {
         public static LogFactory Instance => LazyInstance.Value;
 
-        private static readonly Lazy<LogFactory> LazyInstance = new(BuildLogFactory);
+        private static readonly Lazy<LogFactory> LazyInstance = new Lazy<LogFactory>(BuildLogFactory);
 
-        private const string FileLogLevelKey = "rocketmq.log.level";
+        private const string FileLogLevelKey = "rocketmq_log_level";
         private const string FileLogLevel = "Info";
 
-        private const string ConsoleAppenderEnabledKey = "mq.consoleAppender.enabled";
+        private const string ConsoleAppenderEnabledKey = "mq_consoleAppender_enabled";
         private const string ConsoleAppenderEnabled = "false";
         private const string ConsoleAppenderLogLevel = "Off";
 
 
-        private const string FileLogRootKey = "rocketmq.log.root";
+        private const string FileLogRootKey = "rocketmq_log_root";
 
-        private const string FileMaxIndexKey = "rocketmq.log.file.maxIndex";
+        private const string FileMaxIndexKey = "rocketmq_log_file_maxIndex";
         private const string FileMaxIndex = "10";
 
         private static LogFactory BuildLogFactory()
diff --git a/csharp/rocketmq-client-csharp/Producer.cs b/csharp/rocketmq-client-csharp/Producer.cs
index a1f70989..78ee4e34 100644
--- a/csharp/rocketmq-client-csharp/Producer.cs
+++ b/csharp/rocketmq-client-csharp/Producer.cs
@@ -259,7 +259,7 @@ namespace Org.Apache.Rocketmq
                         throw;
                     }
 
-                    if (exception is not TooManyRequestsException)
+                    if (!(exception is TooManyRequestsException))
                     {
                         // Retry immediately if the request is not throttled.
                         Logger.Warn(e, $"Failed to send message, topic={message.Topic}, maxAttempts={maxAttempts}, " +
@@ -349,7 +349,10 @@ namespace Org.Apache.Rocketmq
         public class Builder
         {
             private ClientConfig _clientConfig;
-            private readonly ConcurrentDictionary<string, bool> _publishingTopics = new();
+
+            private readonly ConcurrentDictionary<string, bool> _publishingTopics =
+                new ConcurrentDictionary<string, bool>();
+
             private int _maxAttempts = 3;
             private ITransactionChecker _checker;
 
diff --git a/csharp/rocketmq-client-csharp/PublishingLoadBalancer.cs b/csharp/rocketmq-client-csharp/PublishingLoadBalancer.cs
index b0cc0d2b..84285999 100644
--- a/csharp/rocketmq-client-csharp/PublishingLoadBalancer.cs
+++ b/csharp/rocketmq-client-csharp/PublishingLoadBalancer.cs
@@ -24,7 +24,7 @@ namespace Org.Apache.Rocketmq
 {
     public class PublishingLoadBalancer
     {
-        private static readonly Random Random = new();
+        private static readonly Random Random = new Random();
 
         private readonly List<MessageQueue> _messageQueues;
         private int _index;
diff --git a/csharp/rocketmq-client-csharp/RpcClient.cs b/csharp/rocketmq-client-csharp/RpcClient.cs
index abf70563..b8a02430 100644
--- a/csharp/rocketmq-client-csharp/RpcClient.cs
+++ b/csharp/rocketmq-client-csharp/RpcClient.cs
@@ -19,6 +19,7 @@ using System;
 using System.Collections.Generic;
 using System.Net.Http;
 using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
 using System.Threading;
 using System.Threading.Tasks;
 using Proto = Apache.Rocketmq.V2;
@@ -55,23 +56,23 @@ namespace Org.Apache.Rocketmq
             }
         }
 
+        private static bool CertValidator(
+            object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+        {
+            // Always return true to disable server certificate validation
+            return true;
+        }
+
         /**
          * See https://docs.microsoft.com/en-us/aspnet/core/grpc/performance?view=aspnetcore-6.0 for performance consideration and
          * why parameters are configured this way.
          */
         internal static HttpMessageHandler CreateHttpHandler()
         {
-            var sslOptions = new SslClientAuthenticationOptions
-            {
-                // Comment out the following line if server certificate validation is required. 
-                // Disable server certificate validation during development phase.
-                RemoteCertificateValidationCallback = (_, _, _, _) => true
-            };
-            var handler = new SocketsHttpHandler
+            // TODO
+            var handler = new HttpClientHandler
             {
-                PooledConnectionIdleTimeout = Timeout.InfiniteTimeSpan,
-                EnableMultipleHttp2Connections = true,
-                SslOptions = sslOptions,
+                ServerCertificateCustomValidationCallback = CertValidator,
             };
             return handler;
         }
diff --git a/csharp/rocketmq-client-csharp/Session.cs b/csharp/rocketmq-client-csharp/Session.cs
index 69a2c197..f41694ec 100644
--- a/csharp/rocketmq-client-csharp/Session.cs
+++ b/csharp/rocketmq-client-csharp/Session.cs
@@ -31,7 +31,7 @@ namespace Org.Apache.Rocketmq
         private static readonly Logger Logger = MqLogManager.Instance.GetCurrentClassLogger();
 
         private static readonly TimeSpan SettingsInitializationTimeout = TimeSpan.FromSeconds(3);
-        private readonly ManualResetEventSlim _event = new(false);
+        private readonly ManualResetEventSlim _event = new ManualResetEventSlim(false);
 
         private readonly AsyncDuplexStreamingCall<Proto::TelemetryCommand, Proto::TelemetryCommand>
             _streamingCall;
diff --git a/csharp/rocketmq-client-csharp/SimpleConsumer.cs b/csharp/rocketmq-client-csharp/SimpleConsumer.cs
index 2146e643..da372177 100644
--- a/csharp/rocketmq-client-csharp/SimpleConsumer.cs
+++ b/csharp/rocketmq-client-csharp/SimpleConsumer.cs
@@ -110,7 +110,7 @@ namespace Org.Apache.Rocketmq
                 State = State.Stopping;
                 Logger.Info($"Begin to shutdown the rocketmq simple consumer, clientId={ClientId}");
                 await base.Shutdown();
-                Logger.Info($"The rocketmq simple consumer starts successfully, clientId={ClientId}");
+                Logger.Info($"Shutdown the rocketmq simple consumer successfully, clientId={ClientId}");
                 State = State.Terminated;
             }
             catch (Exception)
diff --git a/csharp/rocketmq-client-csharp/SubscriptionLoadBalancer.cs b/csharp/rocketmq-client-csharp/SubscriptionLoadBalancer.cs
index e2575a4b..a5a88a6b 100644
--- a/csharp/rocketmq-client-csharp/SubscriptionLoadBalancer.cs
+++ b/csharp/rocketmq-client-csharp/SubscriptionLoadBalancer.cs
@@ -24,7 +24,7 @@ namespace Org.Apache.Rocketmq
 {
     internal sealed class SubscriptionLoadBalancer
     {
-        private static readonly Random Random = new();
+        private static readonly Random Random = new Random();
 
         private readonly List<MessageQueue> _messageQueues;
         private int _index;
diff --git a/csharp/rocketmq-client-csharp/UserAgent.cs b/csharp/rocketmq-client-csharp/UserAgent.cs
index b457b608..9b5a1157 100644
--- a/csharp/rocketmq-client-csharp/UserAgent.cs
+++ b/csharp/rocketmq-client-csharp/UserAgent.cs
@@ -26,7 +26,7 @@ namespace Org.Apache.Rocketmq
         private readonly string _platform;
         private readonly string _hostName;
 
-        public static readonly UserAgent Instance = new();
+        public static readonly UserAgent Instance = new UserAgent();
 
         private UserAgent()
         {
diff --git a/csharp/rocketmq-client-csharp/Utilities.cs b/csharp/rocketmq-client-csharp/Utilities.cs
index d032ae1e..5505a277 100644
--- a/csharp/rocketmq-client-csharp/Utilities.cs
+++ b/csharp/rocketmq-client-csharp/Utilities.cs
@@ -19,8 +19,10 @@ using System.Linq;
 using System.Net.NetworkInformation;
 using System.Text;
 using System;
+using System.Diagnostics;
 using System.IO;
 using System.IO.Compression;
+using System.Security.Cryptography;
 using System.Threading;
 
 namespace Org.Apache.Rocketmq
@@ -45,7 +47,7 @@ namespace Org.Apache.Rocketmq
 
         public static int GetProcessId()
         {
-            return Environment.ProcessId;
+            return Process.GetCurrentProcess().Id;
         }
 
         public static string GetHostName()
@@ -56,13 +58,31 @@ namespace Org.Apache.Rocketmq
         public static string GetClientId()
         {
             var hostName = System.Net.Dns.GetHostName();
-            var pid = Environment.ProcessId;
+            var pid = Process.GetCurrentProcess().Id;
             var index = Interlocked.Increment(ref _instanceSequence);
             var nowMillisecond = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
             var no = DecimalToBase36(nowMillisecond);
             return $"{hostName}@{pid}@{index}@{no}";
         }
 
+        public static string ComputeMd5Hash(byte[] data)
+        {
+            using (var md5 = MD5.Create())
+            {
+                var hashBytes = md5.ComputeHash(data);
+                return BitConverter.ToString(hashBytes).Replace("-", "");
+            }
+        }
+
+        public static string ComputeSha1Hash(byte[] data)
+        {
+            using (var sha1 = SHA1.Create())
+            {
+                var hashBytes = sha1.ComputeHash(data);
+                return BitConverter.ToString(hashBytes).Replace("-", "");
+            }
+        }
+
 
         private static string DecimalToBase36(long decimalNumber)
         {
@@ -92,6 +112,19 @@ namespace Org.Apache.Rocketmq
             return result.ToString();
         }
 
+        public static byte[] CompressBytesGzip(byte[] src, CompressionLevel level)
+        {
+            using (var ms = new MemoryStream())
+            {
+                using (var gzip = new GZipStream(ms, level))
+                {
+                    gzip.Write(src, 0, src.Length);
+                }
+
+                return ms.ToArray();
+            }
+        }
+
         public static byte[] DecompressBytesGzip(byte[] src)
         {
             var inputStream = new MemoryStream(src);
diff --git a/csharp/rocketmq-client-csharp/rocketmq-client-csharp.csproj b/csharp/rocketmq-client-csharp/rocketmq-client-csharp.csproj
index bff3801b..83b0d293 100644
--- a/csharp/rocketmq-client-csharp/rocketmq-client-csharp.csproj
+++ b/csharp/rocketmq-client-csharp/rocketmq-client-csharp.csproj
@@ -1,12 +1,12 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
         <PackageId>RocketMQ.Client</PackageId>
-        <PackageVersion>0.0.11-SNAPSHOT</PackageVersion>
+        <PackageVersion>0.0.14-SNAPSHOT</PackageVersion>
         <Version>$(PackageVersion)</Version>
 
         <Authors>RocketMQ Authors</Authors>
         <Company>Apache Software Foundation</Company>
-        <TargetFramework>net5.0</TargetFramework>
+        <TargetFrameworks>net5.0;netstandard21</TargetFrameworks>
         <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
         <RootNamespace>Org.Apache.Rocketmq</RootNamespace>
         <PackageReadmeFile>README.md</PackageReadmeFile>
@@ -18,30 +18,28 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <None Include="..\README.md" Pack="true" PackagePath="\"/>
-        <PackageReference Include="Crc32.NET" Version="1.2.0"/>
-        <PackageReference Include="Google.Protobuf" Version="3.19.4"/>
-        <PackageReference Include="Grpc.Net.Client" Version="2.43.0"/>
+        <None Include="..\README.md" Pack="true" PackagePath="\" />
+        <PackageReference Include="Crc32.NET" Version="1.2.0" />
+        <PackageReference Include="Google.Protobuf" Version="3.19.4" />
+        <PackageReference Include="Grpc.Net.Client" Version="2.43.0" />
         <PackageReference Include="Grpc.Tools" Version="2.43.0">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>
-        <PackageReference Include="NLog" Version="4.7.13"/>
-        <PackageReference Include="OpenTelemetry" Version="1.3.1"/>
-        <PackageReference Include="OpenTelemetry.Api" Version="1.3.1"/>
-        <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.3.1"/>
+        <PackageReference Include="NLog" Version="4.7.13" />
+        <PackageReference Include="OpenTelemetry" Version="1.3.1" />
+        <PackageReference Include="OpenTelemetry.Api" Version="1.3.1" />
+        <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.3.1" />
 
-        <Protobuf Include="Protos\apache\rocketmq\v2\definition.proto" ProtoRoot="Protos" GrpcServices="Client"/>
-        <Protobuf Include="Protos\google\rpc\code.proto" ProtoRoot="Protos" GrpcServices="Client"/>
-        <Protobuf Include="Protos\google\rpc\error_details.proto" ProtoRoot="Protos" GrpcServices="Client"/>
-        <Protobuf Include="Protos\google\rpc\status.proto" ProtoRoot="Protos" GrpcServices="Client"/>
+        <Protobuf Include="Protos\apache\rocketmq\v2\definition.proto" ProtoRoot="Protos" GrpcServices="Client" />
+        <Protobuf Include="Protos\google\rpc\code.proto" ProtoRoot="Protos" GrpcServices="Client" />
+        <Protobuf Include="Protos\google\rpc\error_details.proto" ProtoRoot="Protos" GrpcServices="Client" />
+        <Protobuf Include="Protos\google\rpc\status.proto" ProtoRoot="Protos" GrpcServices="Client" />
         <Protobuf Include="Protos\apache\rocketmq\v2\service.proto" ProtoRoot="Protos" GrpcServices="Client">
             <Link>Protos\apache\rocketmq\v2\definition.proto</Link>
             <Link>Protos\google\rpc\status.proto</Link>
             <Link>Protos\google\rpc\error_details.proto</Link>
         </Protobuf>
-        <None Update="logo.png" PackagePath="">
-            <Pack>True</Pack>
-        </None>
+        <None Include="logo.png" Pack="true" PackagePath=""/>
     </ItemGroup>
 </Project>
diff --git a/csharp/tests/ConfigFileCredentialsProviderTest.cs b/csharp/tests/ConfigFileCredentialsProviderTest.cs
deleted file mode 100644
index d65439be..00000000
--- a/csharp/tests/ConfigFileCredentialsProviderTest.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System.IO;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Org.Apache.Rocketmq;
-
-namespace tests
-{
-    [TestClass]
-    public class ConfigFileCredentialsProviderTest
-    {
-        [TestInitialize]
-        public void Setup()
-        {
-            var configFilePath = ConfigFileCredentialsProvider.DefaultConfigFilePath();
-            var fileInfo = new FileInfo(configFilePath);
-            var dir = fileInfo.Directory;
-            if (dir != null && !dir.Exists)
-            {
-                dir.Create();
-            }
-
-            var json = "{\"AccessKey\": \"key\", \"AccessSecret\": \"secret\"}";
-            File.WriteAllText(configFilePath, json);
-        }
-
-        [TestMethod]
-        public void TestGetCredentials()
-        {
-            var provider = new ConfigFileCredentialsProvider();
-            var credentials = provider.GetCredentials();
-            Assert.IsNotNull(credentials);
-            Assert.AreEqual(credentials.AccessKey, "key");
-            Assert.AreEqual(credentials.AccessSecret, "secret");
-        }
-
-        [TestCleanup]
-        public void TearDown()
-        {
-            var configFilePath = ConfigFileCredentialsProvider.DefaultConfigFilePath();
-            File.Delete(configFilePath);
-        }
-    }
-}
\ No newline at end of file
diff --git a/csharp/tests/UtilitiesTest.cs b/csharp/tests/UtilitiesTest.cs
new file mode 100644
index 00000000..5c82fcaa
--- /dev/null
+++ b/csharp/tests/UtilitiesTest.cs
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.IO.Compression;
+using System.Text;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Org.Apache.Rocketmq;
+
+namespace tests
+{
+    [TestClass]
+    public class UtilitiesTest
+    {
+        [TestMethod]
+        public void TestDecompressBytesGzip()
+        {
+            var originalData = new byte[] { 1, 2, 3, 4, 5 };
+            var compressedData = Utilities.CompressBytesGzip(originalData, CompressionLevel.Fastest);
+            CollectionAssert.AreEqual(Utilities.DecompressBytesGzip(compressedData), originalData);
+        }
+
+        [TestMethod]
+        public void TestComputeMd5Hash()
+        {
+            var bytes = Encoding.UTF8.GetBytes("foobar");
+            Assert.AreEqual(Utilities.ComputeMd5Hash(bytes), "3858F62230AC3C915F300C664312C63F");
+        }
+
+        [TestMethod]
+        public void TestComputeSha1Hash()
+        {
+            var bytes = Encoding.UTF8.GetBytes("foobar");
+            Assert.AreEqual(Utilities.ComputeSha1Hash(bytes), "8843D7F92416211DE9EBB963FF4CE28125932878");
+        }
+    }
+}
\ No newline at end of file