顯示具有 C# 標籤的文章。 顯示所有文章
顯示具有 C# 標籤的文章。 顯示所有文章

2021年7月28日 星期三

[C#] Check if a collection is empty (null or no elements)

  C#   Check empty collection  


List<stringstrs = null;

if(strs == null || strs.Count > 0)
  Console.WriteLine("1. The list is empty.");
  
if(!(strs?.Count > 0)) 
  Console.WriteLine("2. The list is empty.");
// Notice that when strs is null, 
// strs?.Count will be null and "strs?.Count == 0" will be false.   
if(strs is null || strs is {Count0})
  Console.WriteLine("3. The list is empty.");
  
if(strs is not {Count0})
  Console.WriteLine("4. The list is empty.");






2020年12月27日 星期日

[Notes] NET Conf 2020 Taiwan

 

 .NET 5   NET Conf   2020   C# 9   EF Core 5   ASP.NET Core 

 



 

Notes


 

See my notes on my Github.

 

今年在疫情的影響下,台灣還能正常舉辦實體研討會是很難得跟珍貴的 :)

希望明年全球疫情能逐漸好轉。

 

明年計畫會逐漸將所有和技術有關的文章移至Github,畢竟用Markdown語法寫技術文章比用Blogger快太多了。

Happy New Years and goodbye, 2020!

 

 

 

 

 

 

 

2020年7月14日 星期二

[AutoMapper] Custom Type and Value converter


 .NET Core   .NET   AutoMapper 


Introduction


We can implement the following AutoMapper’s interface to customize the Type or Value converter:


This article will show a few samples for how to use them.


Environment


AutoMapper 8.0.0


Implement


We are going to convert a MyClass1 object to MyClas2 object, that the classes are like following,


MyClass1

public class MyClass1 {
        public string Amount { getset; }
        public string PayDate { getset; }
        public string PayTime { getset; }
    }


MyClass2

public class MyClass2 {
        public decimal Amount { getset; }
        public DateTime PayOn { getset; }
    }



ITypeConverter

If the source and destination classes have too many differences to map their properties, we can implement ITypeConverter to put all the convert logic inside.


public class MyConverter : ITypeConverter<MyClass1MyClass2>
{
    public DtoMerchantCreation Convert(MyClass1 sourceMyClass2 destinationResolutionContext context)
    {
        // Convert MyClass1 instance to MyClass2
    }
}


Usage:

var configuration = new MapperConfiguration(cfg => {
                cfg.CreateMap<MyClass1MyClass2>().ConvertUsing<MyConverter>();
            });




IValueConverter


Implement IValueConverter if the destination property’s value comes from a single property of the source.



public class AmountConverter : IValueConverter<stringdecimal>
{
    public decimal Convert(string sourceMemberResolutionContext context)
    {
         // Convert string to decimal
    }
}


Usage:

var configuration = new MapperConfiguration(cfg => {
        cfg.CreateMap<MyClass1MyClass2>()
                    .ForMember(dest => dest.Amountopt => opt.ConvertUsing(new AmountConverter()));

});



IValueResolver


Implement IValueResolver if the destination property’s value comes from multiple properties of the source.



public class PayOnResolver : IValueResolver<MyClass1MyClass2DateTime>
{
    public DateTime Resolve(MyClass1 sourceMyClass2 destinationDateTime destMemberResolutionContext context)
    {
        string dateTimeStr = $"{source.PayDate} {source.PayTime}+08:00"// e.q. "2020-03-10 08:00:00+08:00"
        return Convert.ToDateTime(dateTimeStr);
    }
}


Usage:

var configuration = new MapperConfiguration(cfg => {
        cfg.CreateMap<MyClass1MyClass2>()
                    .ForMember(dest => dest.PayOnopt => 
                               opt.MapFrom<PayOnResolver>())
});



Notice that if we have the other class: MyClassA, which inherits MyClass1

public class MyClassA : MyClass1 {
    }

Then it can also reuse the PayOnResolver as well. For example,

var configuration = new MapperConfiguration(cfg => {
        cfg.CreateMap<MyClassAMyClass2>()
                    .ForMember(dest => dest.PayOnopt => 
                               opt.MapFrom<PayOnResolver>())
});



Reference








2020年6月13日 星期六

[.NET Core] TLS/SSL Socket programming


 DOTNET Core   Socket   TLS/SSL  






Introduction


This article will shows how to enhance a Socket Server/Client to supports TLS/SSL streaming.

TLS/SSL encrypts the sensitive data being sent between client and server, and authenticate a server.


To enable TLS/SSL streaming in Socket, here are the requirements:

1.  A certificate. (We will use Makecert to create one later.)
2.  Must add the cert to trusted root certificates to bypass “invalid CA“ error or ignore checking the self-signed certificate in client side.

The source code is on my Github: KarateJB/DotNetCore.Socket.Sample, which includes the sample codes of Socket/SSL-Socket server and client.


Environment


.NET Core 3.1.300



Implement



(Optional) Create a self-signed certificate

$ MakeCert -ss Root -sr LocalMachine -a SHA256 -n "CN=127.0.0.1,CN=localhost" -sv local.pvk local.cer -pe -e 12/31/2099 -len 2048
$ pvk2pfx.exe -pvk local.pvk -spc local.cer -pfx local.pfx


The .pfx file (contains public and private keys) is for a Socket server.
The .cer file (contains public key) is for a Socket client.


Server side

First lets create a state-object class for storing buffered streaming data.

SslStramState.cs

public class SslStreamState : IStateObject
{
        /// <summary>
        /// Client Socket
        /// </summary>
        public SslStream SslStream = null;

        private const int FixedBufferSize = 1024;

        /// <summary>
        /// Constructor
        /// </summary>
        public SslStreamState()
        {
            this.Buffer = new byte[this.BufferSize];
            this.Content = new StringBuilder();
        }

        /// <summary>
        /// Size of receive buffer
        /// </summary>
        public int BufferSize
        {
            get { return FixedBufferSize; }
        }

        /// <summary>
        /// Receive buffer
        /// </summary>
        public byte[] Buffer { getset; }

        /// <summary>
        /// Received data string
        /// </summary>
        public StringBuilder Content { getset; }
}



Then create a TCP listener (System.Net.Sockets.TcpListener) to listen for the SSL stream (System.Net.Security.SslStream) as following,


SslSocketServer.cs

using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;

public static class SslSocketServer
{
        public static TcpListener TcpListener = null;

        private const int Port = 6667;
        private const int MaxQueuedClientNumber = 100// Max Queued Clients (that will be waiting for Server to accept and serve)

        static SslSocketServer()
        {
            TcpListener = new TcpListener(System.Net.IPAddress.Any, Port);
        }

        internal static void Start()
        {
            TcpListener.Start(MaxQueuedClientNumber);
        }

        internal static void Stop()
        {
            TcpListener.Stop();
            IsClosed = true;
        }

        internal static void Listen()
        {
            TcpListener.BeginAcceptTcpClient(new AsyncCallback(AcceptCallback), TcpListener);
        }

        private static void AcceptCallback(IAsyncResult ar)
        {
            TcpListener listener = (TcpListener)ar.AsyncState;
            TcpClient handler = listener.EndAcceptTcpClient(ar);

            // SslStream sslStream = new SslStream(client.GetStream(), true);
            var sslStream = new System.Net.Security.SslStream(
                handler.GetStream(), falsenew RemoteCertificateValidationCallback(ValidateServerCertificate), null);

            sslStream.AuthenticateAsServer(new X509Certificate2("Certs/local.pfx", string.Empty), false, SslProtocols.Tls12, false);

            var state = new SslStreamState();
            state.SslStream = sslStream;

            sslStream.BeginRead(state.Buffer, 0, state.BufferSize, new AsyncCallback(ReadCallback), state);
        }

        private static void ReadCallback(IAsyncResult ar)
        {
            var content = string.Empty;

            // Retrieve the state object and the SslStream from the asynchronous state object
            SslStreamState state = (SslStreamState)ar.AsyncState;
            System.Net.Security.SslStream handler = state.SslStream;

            // Read data from the client socket.
            int bytesRead = handler.EndRead(ar);

            if (bytesRead > 0)
            {
                // There  might be more data, so store the data received so far
                state.Content.Append(Encoding.ASCII.GetString(
                    state.Buffer, 0, bytesRead));

                // Check for end-of-file tag. If it is not there, read more data
                content = state.Content.ToString();
                if (content.IndexOf("<EOF>") > -1)
                {
                    // Handle the request (Implement the request handler)
                    using var requestHandler = new MyRequestHandler();
                    requestHandler.HandleAsync(state).Wait();

                    // Echo something back to the client
                    Send(handler, $"Received data on {DateTime.Now.ToString()}");
                }
                else
                {
                    // Not all data received. Get more
                    handler.BeginRead(state.Buffer, 0, state.BufferSize, new AsyncCallback(ReadCallback), state);
                }
            }
        }

        private static void Send(System.Net.Security.SslStream handler, string data)
        {
            // Convert the string data to byte data using ASCII encoding
            byte[] byteData = Encoding.ASCII.GetBytes(data);

            // Begin sending the data to the remote device
            handler.Write(byteData, 0, byteData.Length);
        }

         private static bool ValidateServerCertificate(
            object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            // For self-signed certificate, return true.
            return true;

            if (sslPolicyErrors == SslPolicyErrors.None)
            {
                return true;
            }

            LoggerProvider.Logger.Error($"Certificate validation error: {sslPolicyErrors}");

            return false;
        }
}



Run the TLS/SSL Socket Server

Here is an example of using the Worker Service to startup the Socket server as following,

public class SslSocketWorker : BackgroundService
{
        protected override async Task ExecuteAsync(CancellationToken cancelToken)
        {
            SslSocketServer.Start();

            while (!cancelToken.IsCancellationRequested)
            {
                SslSocketServer.Listen();
            }

            SslSocketServer.Stop();
         
              await Task.CompletedTask;
        }
    }




Client side

The client side creates a TcpClient(System.Net.Sockets.TcpClient) to establish the SSL streaming.

SslSocketClient.cs

using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;

public class SslSocketClient
{
        private const string Host = "127.0.0.1";
        private const int Port = 6667;

        protected async Task SendAsync(byte[] clientData)
        {
            TcpClient client = new TcpClient(Host, Port);

            SslStream sslStream =
                new SslStream(client.GetStream(),
                falsenew RemoteCertificateValidationCallback(ValidateServerCertificate), null);

            var certs = new X509Certificate2Collection();
            certs.Add(new X509Certificate2("Certs/local.cer"));

            await sslStream.AuthenticateAsClientAsync("localhost", certs, System.Security.Authentication.SslProtocols.Tls12, false);

            sslStream.Write(clientData);
            sslStream.Flush();

            // Receive response from server
            byte[] rtnBytes = new byte[1024]; // Data buffer for incoming data
            int bytesRec = sslStream.Read(rtnBytes);
            Console.WriteLine($"Echoed from server: {Encoding.ASCII.GetString(rtnBytes, 0, bytesRec)}");
            sslStream.Close();
            client.Close();

            await Task.CompletedTask;
        }

        private static bool ValidateServerCertificate(
            object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            // For self-signed certificate, always return true.
            return true;

            if (sslPolicyErrors == SslPolicyErrors.None)
            {
                return true;
            }

            return false;
        }
}




An example for sending a message to server and retrieve the respond message back.

MsgSender.cs

public class MsgSender : SslSocketClient
{
        private const string CLIENT_MSG = "Hello, there.";
        public async Task SendMsgAsync()
        {
            // Encode the data string into a byte array
            byte[] byteData = Encoding.ASCII.GetBytes($"{CLIENT_MSG}<EOF>");

            // Send
            await base.SendAsync(byteData);
        }
}






Reference