Monday, 5 September 2022

Database SQL Concepts & key concepts to learn #inprogress

1. Constraints

2. Data Types

3.Diff between where and having clause

1. WHERE Clause is used to filter the records from the table based on the specified condition.HAVING Clause is used to filter record from the groups based on the specified condition.
2.WHERE Clause can be used without GROUP BY ClauseHAVING Clause cannot be used without GROUP BY Clause
3.WHERE Clause implements in row operationsHAVING Clause implements in column operation
4.WHERE Clause cannot contain aggregate functionHAVING Clause can contain aggregate function
5.WHERE Clause can be used with SELECT, UPDATE, DELETE statement.HAVING Clause can only be used with SELECT statement.
6.WHERE Clause is used before GROUP BY ClauseHAVING Clause is used after GROUP BY Clause
7.WHERE Clause is used with single row function like UPPER, LOWER etc.
SELECT COUNT(CustomerID), Country
FROM Customers
where Country='Brazil' or Country!='France'
GROUP BY Country
HAVING COUNT(CustomerID) > 5;
HAVING Clause is used with multiple row function like SUM, COUNT etc.
Below result first group by Country then filter from group data.
SELECT COUNT(CustomerID), Country
FROM Customers
GROUP BY Country
HAVING COUNT(CustomerID) > 5;


4.Temp table Vs CTE (common table expression) Vs Table variable

Temp tables: are created in tempdb, it contains two types #local & ##Global

These tables act as the normal table and also can have constraints, an index like normal tables.

#local temp tables available only for within session alone.

CREATE TABLE #LocalTemp
(
 UserID int,
 Name varchar(50), 
 Address varchar(150)
)
GO
insert into #LocalTemp values ( 1, 'Shailendra','Noida');

##Global temp tables can access to all sessions.

CREATE TABLE ##GlobalTemp
(
 UserID int,
 Name varchar(50), 
 Address varchar(150)
)
GO
insert into ##GlobalTemp values ( 1, 'Shailendra','Noida');
GO
Select * from ##GlobalTemp

CTE: CTE is a named temporary result set which is used to manipulate the complex sub-queries data. This exists for the scope of a statement. This is created in memory rather than the Tempdb database. we cannot create an index on CTE 

CTE improves readability and ease in maintenance of complex queries and sub-queries. Always begin CTE with a semicolon.

;With CTE1(Address, Name, Age)--Column names for CTE, which are optional
AS
(
SELECT Addr.Address, Emp.Name, Emp.Age from Address Addr
INNER JOIN EMP Emp ON Emp.EID = Addr.EID
)
SELECT * FROM CTE1 --Using CTE 
WHERE CTE1.Age > 50
ORDER BY CTE1.NAME

Table Variable: This acts like a variable and exists for a particular batch of query execution. It gets dropped once it comes out of the batch. This is also created in the tempdb database but not the memory. This also allows you to create a primary key, identity at the time of Table variable declaration but not non-clustered index.

GO
 DECLARE @TProduct TABLE
 (
 SNo INT IDENTITY(1,1),
 ProductID INT,
 Qty INT
 ) 
 --Insert data to Table variable @Product 
 INSERT INTO @TProduct(ProductID,Qty)
 SELECT DISTINCT ProductID, Qty FROM ProductsSales ORDER BY ProductID ASC 
 --Select data
 Select * from @TProduct
 
 --Next batch
 GO
 Select * from @TProduct --gives error in next batch

5.Indexes

Indexes are special lookup tables that the database search engine can use to speed up data retrieval. Simply put, an index is a pointer to data in a table. An index in a database is very similar to an index in the back of a book.

6.ACID: properties

A- Atomicity : fail or success it should apply for all transaction not partial.

7. Joins

    i. Inner Join : retrieve only matched rows between tables

    ii. Outer Join : retrieve only Not matched rows between tables

    iii. Left Join : Table A Left join Table B => Retrieve all from Table A and Only Matched rows                     From  Table B.

    iv. Right Join: Vice versa of Left join

8. Union(without duplicate rows) Vs Union All (include duplicate rows)


 

Global Exception Handler in c# .Net Core

 Default exception will handled by ExceptionFilter, incase we need to override this then we can derived from IExceptionFilter and we can customize it.

Predefined interface for Exception filter is below


///predefine interface for exception is below
namespace Microsoft.AspNetCore.Mvc.Filters
{
    //
    // Summary:
    //     A filter that runs after an action has thrown an System.Exception.
    public interface IExceptionFilter : IFilterMetadata
    {
        //
        // Summary:
        //     Called after an action has thrown an System.Exception.
        //
        // Parameters:
        //   context:
        //     The Microsoft.AspNetCore.Mvc.Filters.ExceptionContext.
        void OnException(ExceptionContext context);
    }
}


Let us create a class and inherit from IExceptionFilter file name like GlobalExceptionFilter.cs

Above interface contains  OnException  method so when we derived from tat interface we need to provide definition.



using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Net;
namespace GlobalException
{
public class GlobalExceptionFilter : IExceptionFilter
{

    /// <summary>
    /// Global method to log the Exception thismethod will hit when exception happen
    /// </summary>
    /// <param name="context"></param>
    public void OnException(ExceptionContext context)
    {
        if (context != null)
        {
            /// Find the response status code if it's null then send InternalServerError
            HttpStatusCode statusCode = (context.Exception as WebException != null &&
                        ((HttpWebResponse)(context.Exception as WebException).Response) != null) ?
                        ((HttpWebResponse)(context.Exception as WebException).Response).StatusCode
                        : HttpStatusCode.InternalServerError;

            context.ExceptionHandled = true;
            HttpResponse response = context.HttpContext.Response;
            response.StatusCode = (int)statusCode;
            response.ContentType = "application/json";
            /// use own logger to store exception like below
            //logger.Error(context.Exception);


        }
    }            
}
}

Now we need create Startup.cs class and map in program.cs file like below.

 public class Program
    {
        protected Program()
        {

        }
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }

we mentioned above like all configuration based on Startup class.

Now startup class we need add like below.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Server.IISIntegration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using NLog;
using NLog.Extensions.Logging;
using Newtonsoft.Json.Serialization;
using Microsoft.AspNetCore.Mvc.NewtonsoftJson;
using System;

namespace GlobalExceptionSample.API
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            var config = new ConfigurationBuilder()
          .SetBasePath(System.IO.Directory.GetCurrentDirectory())
          .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).Build();

            LogManager.Configuration = new NLogLoggingConfiguration(config.GetSection("NLog"));

            Configuration = configuration;
           
        }

        public static IConfiguration Configuration { get; set; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            ///any default implementation we can include as AddSingleton like below
            ///services.AddSingleton<ILog, Log>();
            /*----Here we are adding Global Exception filter as GlobalExceptionFilter*/
            services.AddMvc(config => {
                            config.Filters.Add(typeof(GlobalExceptionFilter));
                         });

            services.AddControllers()
                    .AddNewtonsoftJson(options =>
                    {
                        options.SerializerSettings.ContractResolver = new DefaultContractResolver();
                    });

            services.AddCors();
            services.AddAuthentication(IISDefaults.AuthenticationScheme);
           
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseAuthentication();
            app.UseRouting();
            app.UseCors(
              options => options.SetIsOriginAllowed(x => _ = true)
              .AllowAnyMethod()
              .AllowAnyHeader()
              .AllowCredentials());
            app.UseAuthorization();
           
        }
    }
}

so in startup class below highlighted code is called exception filter config.

services.AddMvc(config => {
                            config.Filters.Add(typeof(GlobalExceptionFilter));
                         });



Tuesday, 2 August 2022

Text Overflow hidden with 3dot show content in Auto Tooltip or Title in Angular

Steps:

1. Create one sample application in Angular

2. Generate Components where we need to apply ex: ElipisyfyDemo

3. Generate Directive ex: ng g directive Directives/appEllipsifyMe

Directive code like below:

import {
  Directive, ElementRef, Renderer2,
  HostListener, AfterViewInit }
  from '@angular/core';

@Directive({
selector: '[appEllipsifyMe]'
})
export class EllipsifyMeDirective implements AfterViewInit {
  domElement: any;
constructor(private elementRef: ElementRef,
            private renderer: Renderer2) {
      this.domElement = this.elementRef.nativeElement;
      this.renderer.setProperty(this.domElement.style, 'text-overflow', 'ellipsis');
      this.renderer.setProperty(this.domElement.style, 'overflow', 'hidden');
      this.renderer.setProperty(this.domElement.style, 'white-space', 'nowrap');
    }
ngAfterViewInit(): void {
      this.renderer.setProperty(this.domElement, 'scrollTop', 1);
      //after view init setToolTip function will call based size it will apply.
      this.setToolTip();
   }
/// HostListener will listen whenever UI Resize ex: Minimize or maximize
@HostListener('window:resize', ['$event.target'])
  setToolTip() {
    /// conditional operator when if overflow add title else remove title
      (this.domElement.offsetWidth < this.domElement.scrollWidth) ?
         this.renderer.setAttribute(this.domElement,
                     'title',this.domElement.textContent) :
         this.renderer.removeAttribute(this.domElement, 'title');
   }
}

app.module.ts

Map directive in @NgModule to use it across entire module.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ElipisyfyDemoComponent } from './elipisyfy-demo/elipisyfy-demo.component';
import { EllipsifyMeDirective } from './Directives/app-ellipsify-me.directive';

@NgModule({
  declarations: [
    AppComponent,

    ElipisyfyDemoComponent,
    EllipsifyMeDirective

  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
    ReactiveFormsModule,
   

  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }


Component Html:

<p>elipisyfy-demo works!</p>

<div class="title"  appEllipsifyMe>
    ArulkumarSivaraj@blogspot.com Based on screen size dynamically add tool-tip
    to the element
    when it's getting overflow hidden scenario.
    ArulkumarSivaraj@blogspot.com Based on screen size dynamically add tool-tip
    to the element
    when it's getting overflow hidden scenario.
    ArulkumarSivaraj@blogspot.com Based on screen size dynamically add tool-tip
    to the element
    when it's getting overflow hidden scenario.

</div>

After Run application & Result is below:

Text Overflow Demo













Tuesday, 24 May 2022

.Net Core - Get appsettings..json data in class or controller

 .Net Core applications contains appsettings.json, it contains major configurations related to application, Environment, Database, etc.

Some config key values will be different depends on the environment, example Development, Staging, Production. In this scenario we need maintain key values depends on environment. 

Let see how to overcome this issue, Example DB configuration will be different for every environment so we need maintain different   appsettings.<Environment>.json file like child of appsettings.json.

Based on Environment flag it will override the values i,e take vales from  appsettings.<Environment>.json override in  appsettings.json at runtime.

1.Set Environment Variable

Properties folder -> launchSettings.json file we can set environment values.

Example Set it as Development

{
    "$schema": "https://json.schemastore.org/launchsettings.json",
    "iisSettings": {
      "windowsAuthentication": false,
      "anonymousAuthentication": true,
      "iisExpress": {
        "applicationUrl": "http://localhost:8320",
        "sslPort": 44311
      }
    },
    "profiles": {
      "SolidPrinciples": {
        "commandName": "Project",
        "dotnetRunMessages": true,
        "launchBrowser": true,
        "launchUrl": "swagger",
        "applicationUrl": "https://localhost:7033;http://localhost:5033",
        "environmentVariables": {
          "ASPNETCORE_ENVIRONMENT": "Development"
        }
      },
      "IIS Express": {
        "commandName": "IISExpress",
        "launchBrowser": true,
        "launchUrl": "swagger",
        "environmentVariables": {
          "ASPNETCORE_ENVIRONMENT": "Development"
        }
      }
    }
  }
 

appsetings.json

{
    "Logging": {
      "LogLevel": {
        "Default": "Information",
        "Microsoft.AspNetCore": "Warning"
      }
    },
    "AllowedHosts": "*",
    "Environment": "Prod",
    "ConnectionStrings": {
      ///we need to alter corresponding actual DB connection string.
      "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=Prod;"
    }
  }
 

appsetings.Development.json

{
    "Environment": "Development",
    "ConnectionStrings": {
      ///we need to alter corresponding actual DB connection string.
      "DefaultConnection": "Server=(Development)\\MSSQLLocalDB;Database=Development;"
    }
  }
 

appsetings.Staging.json

{
    "Environment": "Staging",
    "ConnectionStrings": {
      ///we need to alter corresponding actual DB connection string.
      "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=Staging;"
    }
 }
 

In API code we try to consume Environment key from appsetings.

    [Route("api/[controller]")]
    [ApiController]
    public class AppRegionController : ControllerBase
    {
        private readonly ILogger<WeatherForecastController> _logger;
        private readonly IConfiguration _configuration;
        public AppRegionController(ILogger<WeatherForecastController> logger, IConfiguration configuration)
        {
            _logger = logger;
            _configuration = configuration;
        }
        [HttpGet(Name = "ApplicationRegion")]
        public string Get()
        {
            /// it will return value is Development from appsetings.development.json
            var env = _configuration["Environment"];
            var db= _configuration["ConnectionStrings:DefaultConnection"];
            return env;
        }
    }

Now run application and read through api call ,values we will get like below.

Request URL

https://localhost:7033/api/AppRegion

Server response

CodeDetails
200
Response body
Download
Development
Response headers
 content-type: text/plain; charset=utf-8  date: Tue,24 May 2022 10:43:52 GMT  server: Kestrel 

Now change environment to staging like below

{
    "$schema": "https://json.schemastore.org/launchsettings.json",
    "iisSettings": {
      "windowsAuthentication": false,
      "anonymousAuthentication": true,
      "iisExpress": {
        "applicationUrl": "http://localhost:8320",
        "sslPort": 44311
      }
    },
    "profiles": {
      "SolidPrinciples": {
        "commandName": "Project",
        "dotnetRunMessages": true,
        "launchBrowser": true,
        "launchUrl": "swagger",
        "applicationUrl": "https://localhost:7033;http://localhost:5033",
        "environmentVariables": {
          "ASPNETCORE_ENVIRONMENT": "Staging"
        }
      },
      "IIS Express": {
        "commandName": "IISExpress",
        "launchBrowser": true,
        "launchUrl": "swagger",
        "environmentVariables": {
          "ASPNETCORE_ENVIRONMENT": "Staging"
        }
      }
    }
  }

Call same API we will get Staging as response.

Request URL

https://localhost:7033/api/AppRegion

Server response

CodeDetails
200
Response body
Download
Staging

Note:

In case keys not exist in region config but Exist in appsetings.json then it will take it from appsetings.json

Both files exist same then it will override based on environment.

When we deploy all appsettings file exist in build folder.

Environment Based We can enable/Disable feature

Program.cs

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
if(app.Environment.IsStaging())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();






Monday, 9 May 2022

SOLID Principles in C#

SOLID principles provide us with ways to move from tightly coupled code and little encapsulation to the desired results of loosely coupled and encapsulated real needs of a business properly. 

SOLID expansion below 
S: Single Responsibility Principle (SRP) 
O: Open closed Principle (OCP) 
L: Liskov substitution Principle (LSP) 
I: Interface Segregation Principle (ISP) 
D: Dependency Inversion Principle (DIP) 

Single Responsibility Principle (SRP):

SRP suggest only one prime functionality exist in single class & functions.
Single class should not include multiple functions and properties which is not related or common reusable.
This means that every class, or similar structure, in your code should have only one job to do.
Everything in that class should be related to a single purpose.
Example :
Phone number validation, Email validation.
Sometimes we will include these validations within same class like User.class itself but SRP suggest we need to split and create separate class like EmailValidation.class & PhoneNoValidation.class

public class User
{
        public Guid Id { get; set; }

        public string Name { get; set; }
       
        public string Email { get; set; }

        ///Not In SRP
        //public static bool IsValidEmail(string email)
        //{
        //    if (email == null || !email.Contains("@"))
        //    {
        //        return false;
        //    }
        //    return true;
        //}

   }
above class IsValidEmail function is not related user it can be use any email validation so need to create separate one.

public class EmailValidationService
    {
        public static bool IsValidEmail(string email)
        {
            /// sample validation just for understanding SRP
            if (email == null || !email.Contains("@"))
            {
                return false;
            }
            return true;
        }
    }



O: Open/Closed Principle

 
The Open/closed Principle says "A software module/class is open for extension and closed for modification".
 
Here "Open for extension" means, we need to design our module/class in such a way that the new functionality can be added only when new requirements are generated. "Closed for modification" means we have already developed a class and it has gone through unit testing. We should then not alter it until we find bugs. As it says, a class should be open for extensions, we can use inheritance to do this

Best Example ;
1. we need to find interest calculation but each and every bank have their own calculations but outcome result is interest so should not use if ....else conditions for each new banks trying to implement.
2.Find area of the object

Not in open/closed Principle
public class Rectangle{  
   public double Height {get;set;}  
   public double Wight {get;set; }  
}  
public class Circle{  
   public double Radius {get;set;}  
}  
public class AreaCalculator  
{  
   public double TotalArea(object[] arrObjects)  
   {  
      double area = 0;  
      Rectangle objRectangle;  
      Circle objCircle;  
      foreach(var obj in arrObjects)  
      {  
         if(obj is Rectangle)  
         {    
            area += obj.Height * obj.Width;  
         }  
         else  
         {  
            objCircle = (Circle)obj;  
            area += objCircle.Radius * objCircle.Radius * Math.PI;  
         }  
      }  
      return area;  
   }  
}  

Open/ Closed Principle 
public abstract class Shape  
{  
   public abstract double Area();  
}

public class Rectangle: Shape  
{  
   public double Height {get;set;}  
   public double Width {get;set;}  
   public override double Area()  
   {  
      Console.WriteLine ("Rectangle");
      return Height * Width;  
   }  
}  
public class Circle: Shape  
{  
   public double Radius {get;set;}  
   public override double Area()  
   {  
       Console.WriteLine ("Circle");
      return Radius * Radus * Math.PI;  
   }  
}  
 
 /// Main class based on object it will call corresponding class method.

 public class AreaCalculator  
{  
   public double TotalArea(Shape[] arrShapes)  
   {  
      double area=0;  
      foreach(var objShape in arrShapes)  
      {  
         var shapearea = objShape.Area();  
         area+=shapearea;
         Console.WriteLine (shapearea);
      }  
      return area;  
   }  
}  

L: Liskov Substitution Principle

The Liskov Substitution Principle (LSP) states that "you should be able to use any derived class instead of a parent class and have it behave in the same manner without modification". It ensures that a derived class does not affect the behavior of the parent class, in other words,, that a derived class must be substitutable for its base class.
 
This principle is just an extension of the Open Closed Principle and it means that we must ensure that new derived classes extend the base classes without changing their behavior. 


public class SqlFileManager  
{  
   public List<SqlFile? lstSqlFiles {get;set}  
   public string GetTextFromFiles()  
   {  
      StringBuilder objStrBuilder = new StringBuilder();  
      foreach(var objFile in lstSqlFiles)  
      {  
         objStrBuilder.Append(objFile.LoadText());  
      }  
      return objStrBuilder.ToString();  
   }  
   public void SaveTextIntoFiles()  
   {  
      foreach(var objFile in lstSqlFiles)  
      {  
         //Check whether the current file object is read-only or not.If yes, skip calling it's  
         // SaveText() method to skip the exception.  
 
         if(! objFile is ReadOnlySqlFile)  
         objFile.SaveText();  
      }  
   }  
}  

Here we altered the SaveTextIntoFiles() method in the SqlFileManager class to determine whether or not the instance is of ReadOnlySqlFile to avoid the exception. We can't use this ReadOnlySqlFile class as a substitute for its parent without altering SqlFileManager code. So we can say that this design is not following LSP. Let's make this design follow the LSP. Here we will introduce interfaces to make the SqlFileManager class independent from the rest of the blocks.
public interface IReadableSqlFile  
{  
   string LoadText();  
}  
public interface IWritableSqlFile  
{  
   void SaveText();  
}  

public class ReadOnlySqlFile: IReadableSqlFile  
{  
   public string FilePath {get;set;}  
   public string FileText {get;set;}  
   public string LoadText()  
   {  
      /* Code to read text from sql file */  
   }  
}  

public class SqlFile: IWritableSqlFile,IReadableSqlFile  
{  
   public string FilePath {get;set;}  
   public string FileText {get;set;}  
   public string LoadText()  
   {  
      /* Code to read text from sql file */  
   }  
   public void SaveText()  
   {  
      /* Code to save text into sql file */  
   }  
}  

public class SqlFileManager  
{  
   public string GetTextFromFiles(List<IReadableSqlFile> aLstReadableFiles)  
   {  
      StringBuilder objStrBuilder = new StringBuilder();  
      foreach(var objFile in aLstReadableFiles)  
      {  
         objStrBuilder.Append(objFile.LoadText());  
      }  
      return objStrBuilder.ToString();  
   }  
   public void SaveTextIntoFiles(List<IWritableSqlFile> aLstWritableFiles)  
   {  
        foreach(var objFile in aLstWritableFiles)  
        {  
            objFile.SaveText();  
        }  
   }  
}  


I: Interface Segregation Principle (ISP)

 
The Interface Segregation Principle states "that clients should not be forced to implement interfaces they don't use. Instead of one fat interface, many small interfaces are preferred based on groups of methods, each one serving one submodule.".
Example:
public Interface IScrum  
{  
   void CreateSubTask();  
   void AssginTask();  
   void WorkOnTask();  
}  

public class TeamLead : IScrum  
{  
   public void AssignTask()  
   {  
      //Code to assign a task.  
   }  
   public void CreateSubTask()  
   {  
      //Code to create a sub task  
   }  
   public void WorkOnTask()  
   {  
      //Code to implement perform assigned task.  
   }  
}  

public class Manager: IScrum  
{  
   public void AssignTask()  
   {  
      //Code to assign a task.  
   }  
   public void CreateSubTask()  
   {  
      //Code to create a sub task.  
   }  
   public void WorkOnTask()  
   {  
       //// here forced to implement so not in ISP
      throw new Exception("Manager can't work on Task");  
   }  
}  

Avoid to throw exception we split the interface 
public interface IProgrammer  
{  
   void WorkOnTask();  
}
public interface ILead  
{  
   void AssignTask();  
   void CreateSubTask();  
}  

public class Programmer: IProgrammer  
{  
   public void WorkOnTask()  
   {  
      //code to implement to work on the Task.  
   }  
}  
public class Manager: ILead  
{  
   public void AssignTask()  
   {  
      //Code to assign a Task  
   }  
   public void CreateSubTask()  
   {  
   //Code to create a sub taks from a task.  
   }  
}  

public class TeamLead: IProgrammer, ILead  
{  
   public void AssignTask()  
   {  
      //Code to assign a Task  
   }  
   public void CreateSubTask()  
   {  
      //Code to create a sub task from a task.  
   }  
   public void WorkOnTask()  
   {  
      //code to implement to work on the Task.  
   }  
}  

D: Dependency Inversion Principle

 
The Dependency Inversion Principle (DIP) states that high-level modules/classes should not depend on low-level modules/classes. Both should depend upon abstractions. Secondly, abstractions should not depend upon details. Details should depend upon abstractions.

Example: Exception log, error.log(exception ex) should be single function but within that based on cofiguration we alter storage place.
Not in DIP
public class DataExporter  
{  
   public void ExportDataFromFile()  
   {  
      try {  
         //code to export data from files to database.  
      }  
      catch(IOException ex)  
      {  
         new ExceptionLogger().LogIntoDataBase(ex);  
      }  
      catch(Exception ex)  
      {  
         new ExceptionLogger().LogIntoFile(ex);  
      }  
   }  
}  

DIP:

public class DataExporter  
{  
   public void ExportDataFromFile()  
   {  
      try {  
         //code to export data from files to database.  
      }  
      catch(Exception ex)  
      {  
         new ExceptionLogger().LogException(ex);  
      }  
     
   }  
}  

Public interface ILogger
{
    void LogException(Exception exception);
}
public class FileLog : ILogger
{  
   
   public void LogException()  
   {  
      //defalut logger
      ///logic for Write exception into files
   }
}

public class ExceptionLogger  
{  
   private ILogger _logger;
   public ExceptionLogger()  
   {  
      //defalut logger
      this._logger = new FileLog();  
   } 
   public ExceptionLogger(ILogger aLogger)  
   {  
      this._logger = aLogger;  
   }  
   public void LogException(Exception exception)  
   {  
       //based on implemeted logger it will log, Ex: log exception in DB,File,Mail etc
      this._logger.LogMessage(exception);  
   }  
 
}