Thursday 19 December 2019

SOLID Design Principles in C# - Single Responsibility Principle (SRP)


A class or module should have one, and only one, reason to be changed.

Usually a developer writes the code with their best knowledge, sometimes they end up building the class which will perform the multiple responsibilities.

Everything works fine in the initial step, but later if the requirement changes then class maintenance and testing will be difficult to manage.

Real Time Example:
For instance in start up company few can manage all the responsibilities of it. 
But if you consider in a big organization they have different departments managed by the skilled professional who are expertise in respective areas.So that everyone handles individual responsibilities rather than handling multiple, by this maintenance will be easier, deliverable, issue fixes, managing success rate leads to company growth.  

Class with multiple responsibilities


internal class EmployeeHandler
{
    internal string EmployeeName { get; set; }
    internal string Address { get; set; }

    internal void SaveEmployeeDetails()
    {
        // Code to save the details in Database
        // Connect to Database and open DB connection
        // Logical checks
        // Process DB oprations to DB
        // Close the connection
    }
}

The above class is violating the Single Responsibility Principle because of the following reasons.
    1. We have to changes the class if we need to add new attributes.
    2. If any database changes occurred we need to modify this class.
    3. Overtime the class size will expand and maintenance will be hard.

What's the solution?

Designing the classes with Single Responsibility


internal class EmployeeDetails
{
    internal string EmployeeName { get; set; }
    internal string Address { get; set; }
}

internal class PersistEmployee
{
    internal void SaveEmployeeDetails()
    {
        // Code to save the details in Database
        // Connect to Database and open DB connection
        // Logical checks
        // Process DB operations to DB
        // Close the connection
    }
}

Benefits of above classes:

1. The changes occurred in Employee Details class doesn't need to take care in another class. Like wise changes occurred in the class connected to database class doesn't need to take care in employee details class.  
2. The code is simpler, easy to maintain and understand.

Conclusion — Single responsibility is most considerable principle in code refactoring. This makes our code self explanatory, decouple the classes and easy to maintain.  

Please check my below posts on other SOLID principles for better understanding
Open/Closed Principle (OCP)


Interface Segregation Principle (ISP)

Wednesday 18 December 2019

Design Patterns - Singleton Design Pattern

Objective: Ensure a class only has one instance, and provides a global point of access to it.

Characteristics.

  • The class must be sealed.
  • A single constructor, that is private and parameterless.
  • A static variable that holds a reference to the single created instance, if any.
  • A public static means of getting the reference to the single created instance, creating one if necessary.

Non Thread Safe Singleton Design Pattern
namespace DesignPatterns
{
    public sealed class Singleton
    {
        // This static variable holds one instance of a class
        private static Singleton instance;

        // Private constructor will control the instantiation
        private Singleton() { }

        // Property responsible to create the single instace and return
        public static Singleton Instance
        {
            get
            {
                // If the static instance is null which means the instance is not yet created.
                if (instance == null)
                {
                    // Creats the static instance with the private constructor.
                    // This is the lazy object instance creation, 
                    // this will creates the object when the property is been called.
                    instance = new Singleton();
                }

                // The instace is ready, the static instance will return from here.
                return instance;
            }
        }

        // Other class methods.....
    }
}
This will make sure the single instance is created but it is not thread safe.
Thread Safe Singleton Design Pattern
public sealed class Singleton
    {
        private static readonly Singleton instance;

        // a static constructor is guaranteed to be called only once per AppDomain, 
        // and only when it’s needed (a static member is called or an instance of that object is created).
        static Singleton()
        {
            instance = new Singleton();
        }

        private Singleton() { }

        public static Singleton Instance
        {
            get
            {
                return instance;
            }
        }

        // Other class methods.....
    }
Benefits of Singleton Design Pattern:

1. Instance Controlling: This prevents other objects from instantiating their own copies of the Singleton object, ensures that all objects access with the single instance.

2. Flexibility: Since the class controls the instantiation process, the class has the flexibility to change the instantiation process.
3. Helpful in controls concurrent access to a shared resource like logging, registry settings, file system, windows manager etc.
4. Managing the multi-thread Pool.
5. It can be lazy loaded.
6. It has Static Initialization.
7. It helps to hide dependencies.
8. It provides a single point of access to a particular instance, so it is easy to maintain.

Example:

Consider logging process in an application. The log files are shared globally and bigger in size. Your intention is want to flush, sync and close it properly. This is an example of a single shared resource that has to be managed.

Thursday 12 December 2019

C#.NET - Working With Files, File and Directory

The following class has the code snippets to perform the below operations.
  • Move all files from one directory to another directory
  • Moves source file to target directory
  • Copies all files from one directory to another directory
  • Copies source file to target directory
  • Creates a Directory if it is not existed
  • Deletes the file from path

using System.IO;
using System.Linq;

public class FileProcessor
{
    /// <summary>
    /// Move all files from one directory to another directory
    /// </summary>
    /// <param name="sourceDir">Source Directory</param>
    /// <param name="targetDir">Target Directory</param>
    /// <param name="overWrite">true| overwrite the file. false|</param>
    public static void MoveFiles(string sourceDir, string targetDir, bool overWrite = false)
    {
        if (!Directory.Exists(sourceDir))
        {
            return;
        }

        CheckDirectory(targetDir);

        string[] filesToMove = Directory.GetFiles(sourceDir);

        if (filesToMove != null && filesToMove.Any())
        {
            string destinationFilePath;
            filesToMove.ToList().ForEach(
                filePath =>
                {
                    destinationFilePath = Path.Combine(targetDir, Path.GetFileName(filePath));
                    if (File.Exists(destinationFilePath))
                    {
                        if (overWrite)
                        {
                            DeleteFile(destinationFilePath);
                            File.Move(filePath, destinationFilePath);
                        }
                    }
                    else
                    {
                        File.Move(filePath, destinationFilePath);
                    }
                });
        }
    }

    /// <summary>
    /// Moves source file to target directory
    /// </summary>
    /// <param name="filepath"></param>
    /// <param name="targetDirectory"></param>
    /// <param name="overWrite"></param>
    public static void MoveFile(string sourceFilePath, string targetDirectory, bool overWrite = false)
    {
        if (!File.Exists(sourceFilePath))
        {
            return;
        }

        CheckDirectory(targetDirectory);

        string destinationFilePath = Path.Combine(targetDirectory, Path.GetFileName(sourceFilePath));

        if (overWrite)
        {
            DeleteFile(destinationFilePath);
        }

        File.Move(sourceFilePath, destinationFilePath);
    }

    /// <summary>
    /// Copies all files from one directory to another directory
    /// </summary>
    /// <param name="sourceDir"></param>
    /// <param name="destDir"></param>
    /// <param name="overWrite"></param>
    public static void CopyFiles(string sourceDir, string destDir, bool overWrite = false)
    {
        if (!Directory.Exists(sourceDir))
        {
            return;
        }

        CheckDirectory(destDir);

        string[] filesToCopy = Directory.GetFiles(sourceDir);

        if (filesToCopy != null && filesToCopy.Any())
        {
            filesToCopy.ToList().ForEach(
                sourceFilePath =>
                {
                    File.Copy(sourceFilePath, Path.Combine(destDir, Path.GetFileName(sourceFilePath)), overWrite);
                });
        }
    }

    /// <summary>
    /// Copies source file to target directory
    /// </summary>
    /// <param name="sourceFilePath"></param>
    /// <param name="directoryToCopy"></param>
    /// <param name="overWrite"></param>
    /// <returns></returns>
    public static void CopyFile(string sourceFilePath, string directoryToCopy, bool overWrite = false)
    {
        if (!File.Exists(sourceFilePath))
        {
            return;
        }

        CheckDirectory(directoryToCopy);

        string destinationFilePath = Path.Combine(directoryToCopy, Path.GetFileName(sourceFilePath));
        File.Copy(sourceFilePath, destinationFilePath, overWrite);
    }

    /// <summary>
    /// Creates a Directory if it is not existed
    /// </summary>
    /// <param name="directoryToCopy"></param>
    public static void CheckDirectory(string directoryToCopy)
    {
        if (!Directory.Exists(directoryToCopy))
        {
            Directory.CreateDirectory(directoryToCopy);
        }
    }

    /// <summary>
    /// Deletes the file from path
    /// </summary>
    /// <param name="filepath"></param>
    public static void DeleteFile(string filepath)
    {
        if (File.Exists(filepath))
        {
            File.Delete(filepath);
        }
    }
}

C#.NET - Get All Files from the Directory with File Types Filter

The below code snippet will load all files from the given directory and filter the files by the given file type filter and returns the files info.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

public class FileProcessor
{
    /// <summary>
    /// Loads files by given filter
    /// </summary>
    /// <param name="directoryPath"></param>
    /// <param name="fileTypes"></param>
    /// <param name="caseSensitive"></param>
    /// <returns></returns>
    public IEnumerable<string> LoadFilesByFilters(string directoryPath, string[] fileTypes, bool caseSensitive = false)
    {
        StringComparer compare = StringComparer.OrdinalIgnoreCase;

        if (caseSensitive)
        {
            compare = StringComparer.Ordinal;
        }

        return Directory.EnumerateFiles(directoryPath, "*.*", SearchOption.TopDirectoryOnly)
                    .Where(file => fileTypes.Contains(Path.GetExtension(file), compare));
    }

    /// <summary>
    ///  How to Use?
    /// </summary>
    public IEnumerable<string> LoadFiles()
    {
        return LoadFilesByFilters(@"C:\Users\sravilla\Desktop\Repository", new[] { "*.xls", "*.xlsx" });
    }

}

C#.NET - How to Create a Zip File using Compression Classes

The following code snippet takes the specified file (sourceFilePath) and creates Zip file.

using System.IO;
using System.IO.Compression;

public class FilesProcess
{
    public bool ConvertFileToZip(string sourceFilePath, string destinationZipFilePath)
    {
        bool isFileZipped = false;

        using (ZipArchive archive = ZipFile.Open(destinationZipFilePath, ZipArchiveMode.Create))
        {
            archive.CreateEntryFromFile(sourceFilePath, Path.GetFileName(sourceFilePath));
        }

        if (File.Exists(destinationZipFilePath))
        {
            isFileZipped = true;
        }
        return isFileZipped;
    }
}

C#.NET - Regular Expressions - Search Text by Group Name

The following code snippet will guide you on how to search a string with Regular Expressions with Grouping mechanism and return the value by the group name?
public string RegExGetValueByGroupName(string input, string regExPattern, string groupName)
{
   IEnumerable<Group> matchedList = new Regex(regExPattern)
.Match(input).Groups.Where(grp => string.Equals(grp.Name, groupName));
            return matchedList.Any() ? matchedList.FirstOrDefault().Value : null;
}

C#.NET - Reflection - Get Enum Description from Enum Value

/// Gets the description of the Enumeration provided.
public string GetEnumDescription(Enum value) { return value .GetType() .GetMember(value.ToString()) .FirstOrDefault() .GetCustomAttribute() ?.Description; }

C#.NET - Reflection - Get Enum Value from Description Attribute

Below snippet will get the Enum value by the Enum description
public T GetEnumValueFromDescription(string description)
{
    Type type = typeof(T);
    if (!type.IsEnum)
    {
        throw new ArgumentException();
    }

    FieldInfo[] fields = type.GetFields();
    var field = fields
                    .SelectMany(f => f.GetCustomAttributes(
                        typeof(DescriptionAttribute), false), (
                            f, a) => new { Field = f, Att = a })
                    .Where(a => ((DescriptionAttribute)a.Att)
                        .Description == description).SingleOrDefault();
    return field == null ? default(T) : (T)field.Field.GetRawConstantValue();
}

C#.NET - Large Files Search Processing with Boyer-Moore Searching Algorithm

The below class searches the given string in the specified file. This algorithm is implemented with Boyer-Moore. 

All you need to copy the below full class and call it as shown below.

long searchCount = FileGrep.GetMatchIndexes(filePath, "Hyderabad").Count;

This program can process huge files in seconds.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;

namespace FileProcessor
{
    public class FileGrep
    {
        #region Public Methods
        /// 
        /// Return all matches of the pattern in specified file using the Boyer-Moore algorithm
        /// 
        /// The file to be searched
        /// The size of the buffer to use when reading the file
        /// IEnumerable which returns the indexes of pattern matches
        public static ReadOnlyCollection GetMatchIndexes(string filePath, string searchFor, int bufferSize = 1024 * 1024)
        {
            List matchIndexes = new List();

            if (string.IsNullOrEmpty(searchFor))
            {
                return new ReadOnlyCollection(matchIndexes);
            }

            FileInfo fileToSearch = new FileInfo(filePath);
            if (!fileToSearch.Exists)
            {
                throw new FileNotFoundException();
            }

            if (bufferSize  (Int32.MaxValue - (searchPattern.Length - 1)))
            {
                throw new ArgumentOutOfRangeException("bufferSize", bufferSize, string.Format("Size of the file buffer ({0}) plus the size of the search pattern minus one ({1}) may not exceed Int32.MaxValue ({2}).", bufferSize, (searchPattern.Length - 1), Int32.MaxValue));
            }

            using (FileStream stream = fileToSearch.OpenRead())
            {
                // Make sure that the file stream is seekable to access the data
                if (!stream.CanSeek)
                {
                    throw new Exception(String.Format("The file '{0}' is not seekable!  Search cannot be performed.", fileToSearch));
                }

                int chunkIndex = 0;
                while (true)
                {
                    byte[] fileData = GetNextChunkForSearch(stream, chunkIndex, bufferSize, searchPattern.Length);

                    if (fileData == null || !fileData.Any())
                    {
                        //EOF, so exit
                        break;
                    }

                    List occuranceIndexes = GetMatchIndexes_Internal(fileData, searchPattern, goodSuffixShift, badCharacterShift);

                    if (occuranceIndexes != null)
                    {
                        // We found one or more matches in our buffer.  Translate the buffer index
                        // back to the file index by adding the buffer size * the chunk index.
                        int bufferOffset = (bufferSize * chunkIndex);
                        matchIndexes.AddRange(occuranceIndexes.Select(bufferMatchIndex => (bufferMatchIndex + bufferOffset)));
                    }
                    chunkIndex++;
                } // end while
            }
            return new ReadOnlyCollection(matchIndexes);
        }

        #endregion Public Methods

        #region Helpers
        /// 
        /// Build the bad byte shift array.
        /// 
        /// Pattern for search
        /// Bad byte shift array
        private static long[] BuildBadCharacterShift(byte[] pattern)
        {
            long[] badCharacterShift = new long[256];
            long patternLength = Convert.ToInt64(pattern.Length);

            for (long c = 0; c 
        /// Find suffixes in the pattern
        /// 
        /// Pattern for search
        /// Suffix array
        private static long[] FindSuffixes(byte[] pattern)
        {
            long f = 0;

            long patternLength = Convert.ToInt64(pattern.Length);
            long[] suffixes = new long[pattern.Length + 1];

            suffixes[patternLength - 1] = patternLength;
            long g = patternLength - 1;
            for (long i = patternLength - 2; i >= 0; --i)
            {
                if (i > g && suffixes[i + patternLength - 1 - f] = 0 && (pattern[g] == pattern[g + patternLength - 1 - f]))
                    {
                        --g;
                    }
                    suffixes[i] = f - g;
                }
            }

            return suffixes;
        }
        /// 
        /// Build the good suffix array.
        /// 
        /// Pattern for search
        /// Suffixes in the pattern
        /// Good suffix shift array
        private static long[] BuildGoodSuffixShift(byte[] pattern, long[] suff)
        {
            long patternLength = Convert.ToInt64(pattern.Length);
            long[] goodSuffixShift = new long[pattern.Length + 1];

            for (long i = 0; i = -1; --i)
            {
                if (i == -1 || suff[i] == i + 1)
                {
                    for (; j 
        /// Gets the next chunk of bytes from the stream to perform the search.
        /// 
        /// The stream containing the file to search
        /// The index of the chunk to read from the file
        /// The size of the data chunk to read from the file
        /// The bytes read out of the stream
        private static byte[] GetNextChunkForSearch(Stream stream, int chunkIndex, int fileSearchBufferSize, int searchPatternLength)
        {
            byte[] chunk = null;

            long fileStartIndex = Convert.ToInt64(chunkIndex) * Convert.ToInt64(fileSearchBufferSize);
            if (fileStartIndex = searchPatternLength)
                {
                    // If we read less than the buffer length (end of file), then return a trimmed byte array.
                    if (numBytesRead 
        /// Return all matches of the pattern in specified data using the Boyer-Moore algorithm
        /// 
        /// The data to be searched
        /// List which returns the indexes of pattern matches
        private static List GetMatchIndexes_Internal(byte[] dataToSearch, byte[] searchPattern, long[] goodSuffixShift, long[] badCharacterShift)
        {
            List matchIndexes = new List();

            long patternLength = Convert.ToInt64(searchPattern.Length);
            long textLength = Convert.ToInt64(dataToSearch.Length);

            /* Searching */
            long index = 0;
            while (index = 0)
                {
                    if (searchPattern[unmatched] != dataToSearch[unmatched + index])
                    {
                        //pattern mismatch
                        index += Math.Max(goodSuffixShift[unmatched], badCharacterShift[dataToSearch[unmatched + index]] - patternLength + 1 + unmatched);
                        break;
                    }
                    unmatched--;
                }

                if (unmatched < 0)
                {
                    //match found
                    matchIndexes.Add(index);
                    index += goodSuffixShift[0];
                }
            }

            return matchIndexes;
        }
        #endregion Helpers
    }

}