Search This Blog

2013/11/08

Extension Method in C#?


Extension method is special kind of static method in c# which even though defined as static can be called as if instance methods. It is new feature in C# 3.0 or .Net 3.5
    Most fascinating thing about extension method is it let us to add a new method to existing type (class or interface) without modifying it or creating a derived type. It can be used to extend any type in the .NET Framework or any custom type.
How to Define Custom Extension Method?
Consider code below where we are defining a static class ‘MyStringHelper’  in which we are defining an extension method called ‘SplitCamelCase’.
The first parameter here ‘string’  which specifies it operates on string type or in other word it going to extend existing type called ‘string’, but its syntax says it always need to be preceded by the ‘this’ modifier.
  I am creating a console application and adding a new class to it namely Class1.cs.
Code in both files listed below showing implementation of extension method with 

       1)      Native .Net Type string
       2)      Custom Class
       3)      Custom Interface

[Program.cs]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OtherExtensionMethodNamespace;
using System.Text.RegularExpressions;

namespace ExtensionMethod
{
    class Program
    {
        static void Main(string[] args)
        {
            MyMath MyMathObj= new MyMath();
            IIntMath IIntMathObj = new IntMath();

            Console.WriteLine("Call to Extended Method On Class Implementing Interface:" + IIntMathObj.SubstractInt(10, 2).ToString());
            Console.WriteLine("Call to Extended Method on Class:" + MyMathObj.addIntMinusOne(10,2).ToString());

            Console.WriteLine( "Call to extension Method on Native String Type:" +"cameraMountedGoogleCarOnStreet".SplitCamelCase());
            Console.ReadKey();
        }
    }
   
    /*extension methods in same namespace*/

    /*Extension method & Interface*/
    interface IIntMath
    {
        int addInt(int i, int j);
    }
    class IntMath : IIntMath
    {
        public int addInt(int i, int j)
        {
          return  i + j;
        }
    }

    /*Extension method & custom type*/
    class MyMath
    {
        public int addIntPlusOne(int i, int j)
        {
            return i + j+1;
        }
    }
  

    /*Extension method & .net type*/
    static class StringHelper65
    {
        public static string SplitCamelCase(this string str)
        {
            return Regex.Replace(Regex.Replace(str, @"(\P{Ll})(\P{Ll}\p{Ll})", "$1 $2"), @"(\p{Ll})(\P{Ll})", "$1 $2");
        }
    }
}

[Class1.cs]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ExtensionMethod;

/*extension methods Written in Other namespace*/
namespace OtherExtensionMethodNamespace
{
    static class StringHelper45
    {
        public static int addIntMinusOne(this MyMath MyMathObj, int i, int j)
        {
            return i - j - 1;
        }

        public static int SubstractInt(this IIntMath IntMathObj, int i, int j)
        {
            return i -j;
        }
    }
}
As class StringHelper45 & StringHelper65 are in different namespace we have to add using directive to make required type visible in both files.
Can we override an instance method in given type by the Extension Method?
When we write an extension method with same signature & name as any instance method whenever we attempt to call the newly created extension method, we are going to use instance method calling syntax as follows 

OurClass OurObj= new OurClass();
OurObj.ExtensionMethodName(); 

But compiler checks if native instance method exists of this name & signature, if so then it will ignore any of its extension method implementation. If no such instance method found then only it uses extension method.
Extension methods always have lower priority than instance methods defined in the type itself.
     Microsoft guideline says that one should use extension methods sparingly. Whenever possible, client code that must extend an existing type should do so by creating a new type derived from the existing type.
   Extension method are used to write custom html helper like html.textboxfor in mvc.

2013/11/05

DML Operations with Ado.NET Entity Model

Today I am going to explore the Insert ,update,delete operations using ADO.NET Data Entity Model.
Our main focus is do insert ,update & delete successfully,In latter POST we will try to more consuming topic like transcation.
First we will create 5 tables in our database say TestBed 

create table Course( Id int not null identity(1,1)primary key, Name varchar(50), Duration int)
create table Student( Id int not null identity(1,1) primary key, Name varchar(50), EntryDate datetime)
create table CourseToStudent(Id int not null identity(1,1), CourseId int references Course(Id),StudentId int references Student(Id),StartDate datetime)
create table Course2Student(Id int not null identity(1,1),C2SId int references CourseToStudent(Id))
create table DummyTrail(id int identity(1,1) primary key,CourseName varchar(50),DeletedOn Datetime)
Add some data into them to resemble below stated
 Student:
Id
Name
EntryDate
1
sagar
2013-11-05
2
sangram
2013-11-05
3
sachin
2013-11-05
4
swapnil
2013-11-05
  Course:
Id
Name
Duration
1
C
20
2
C++
30
3
VB 6.0
40
4
VC ++
60
5
C#
60
6
VB.NET
60
7
ASP.NET
60
8
PHP 5.0
40
   CourseToStudent:

Id
CourseId
StudentId
StartDate
1
1
1
2013-11-05
2
1
2
2013-11-05
3
1
3
2013-11-05
4
1
4
2013-11-05
    
   Now we will add Ado.net to entity data model to our brand new console application
To Demonstrate Insert ,update & delete with entity data model.

 I am going to create some static functions for each i.e. for insert ,update & delete.
Code for them is as follows
class Program
    {
   static void Main(string[] args)
   {
   Console.WriteLine("Existing Course Collection is");
   ShowAllCourses();

   Console.WriteLine("Adding Node.js Course");
   AddNodeJs();
   ShowAllCourses();
   Console.WriteLine("Updating Course Name from 'Node.js' to 'NODE JS'");
   UpdateNodeJs();
   ShowAllCourses();
   Console.WriteLine("Removing Course 'NODE JS'");
   //DeleteNodeJs();
   DeleteNodeJs();
   ShowAllCourses();
   Console.ReadKey();
   }

    private static void ShowAllCourses()
    {
      Console.WriteLine("------------------------------------");
      //create DataContext object
      TestBedEntities DataContext = new TestBedEntities();
      var AllCourses = from m in DataContext.Courses
                      select new { CourseId = m.Id, 
                         CourseName = m.Name, 
                         CourseDuration = m.Duration };
      foreach (var course in AllCourses)
      {
        Console.Write("Id: " + course.CourseId);
        Console.Write(", Name: " + course.CourseName);
        Console.WriteLine(", Duration: " + course.CourseDuration);
      }
      Console.WriteLine("--------------------------------");
    }

    //Add New Records
    private static void AddNodeJs()
    {
      TestBedEntities DataContext = new TestBedEntities();
      Course NodeJsCourse = new Course();

     //add new course namely node.js
     NodeJsCourse.Name = "Node.js";
     NodeJsCourse.Duration = 50;
     DataContext.Courses.Add(NodeJsCourse);
     DataContext.SaveChanges();

     //enroll student to node.js course
     CourseToStudent c2s1 = new CourseToStudent 
                    {CourseId = NodeJsCourse.Id, 
                     StartDate = DateTime.Now,StudentId = 2 };
     DataContext.CourseToStudents.Add(c2s1);

     //create a obstable for deleteing
     Course2Student obst = new Course2Student{C2SId=c2s1.Id };
     DataContext.Course2Student.Add(obst);
     DataContext.SaveChanges();
   }

   //update existing record
   private static void UpdateNodeJs()
    {
   TestBedEntities DataContext = new TestBedEntities();
   Course NodejsCourse = DataContext.Courses.
                  FirstOrDefault(m => m.Name=="Node.js");
   if (NodejsCourse != null)
   {
    NodejsCourse.Name = "NODE js";
    DataContext.SaveChanges();
   }
  }
   //delete record
   /*private static void DeleteNodeJs()
     {
  //Data maping object to our database
  TestBedEntities DataContext = new TestBedEntities();
  Course NodeJsCourse = DataContext.Courses.FirstOrDefault(m => m.Name== "NODE js");

  if (NodeJsCourse != null)
  {
    DataContext.Courses.Remove(NodeJsCourse);
    DataContext.SaveChanges();
  }
 }*/

    public static void DeleteNodeJs()
    {
      //Data maping object to our database
       TestBedEntities DataContext = new TestBedEntities();
       Course NodeJsCourse = DataContext.Courses.FirstOrDefault(m => m.Name 
                                                               == "NODE js");

        int NodeJsCourseId = NodeJsCourse.Id;
        CourseToStudent NodeJsCourseStudents = DataContext.CourseToStudents.
             FirstOrDefault(m=>m.CourseId == NodeJsCourseId);
        Course2Student NodeJsCourse2Students = DataContext.Course2Student.
             FirstOrDefault(m => m.C2SId == NodeJsCourseStudents.Id);
        DummyTrail MyDummyTrail = new DummyTrail{CourseName=NodeJsCourse.Name,
             DeletedOn=DateTime.Now};

         if ((NodeJsCourseStudents != null) && (NodeJsCourse != null))
           {
              using (TransactionScope scope = new TransactionScope())
               {
               try
               {
                //first try to delete student in course
                DataContext.DummyTrails.Add(MyDummyTrail);
                DataContext.Course2Student.Remove(NodeJsCourse2Students);
                DataContext.CourseToStudents.Remove(NodeJsCourseStudents);
                            
                //if succeed try to delete course
                DataContext.Courses.Remove(NodeJsCourse);
                DataContext.SaveChanges();
                scope.Complete();
               }
               catch (Exception exp)
               { 
               }
               finally
               {
                scope.Dispose();
               }
              }
           }
        }
    }
Lets explore the methods written above

1) AddNodeJs: This methods Insert database operation it adds new record to course table and then enrolls an existing student to newly added course Course2Student is table which has two columns only one is primary key & other is foreign key from CoursetoStudent table. Purpose of this table is to create an obstacle for directly deleting a record from CoursetoStudent so that we can latter work on something similar to implementing transaction.

2) UpdateNodeJs: This method demonstrate database update operation it first chooses record for Course “Node.js” and update its name to “NODE Js”

3) DeleteNodeJs: This method deletes the record from Course2Student table first then from CourseToStudent table & then Course table ,to satisfy foreign key constraints set on table schema.
     At the same time it insert the deleted record into DummyTrail table Try commenting code for deleteting Course2Students but try to delete from Course2Students it will throw an exception

public static void DeleteNodeJs()
{
//Data maping object to our database
TestBedEntities DataContext = new TestBedEntities();
Course NodeJsCourse = DataContext.Courses.FirstOrDefault(m => m.Name == "NODE js");

int NodeJsCourseId = NodeJsCourse.Id;
CourseToStudent NodeJsCourseStudents = DataContext.CourseToStudents.FirstOrDefault(m=>m.CourseId == NodeJsCourseId);

//Course2Student NodeJsCourse2Students = DataContext.Course2Student.
             FirstOrDefault(m => m.C2SId == NodeJsCourseStudents.Id);

DummyTrail MyDummyTrail = new DummyTrail { CourseName=NodeJsCourse.Name,DeletedOn=DateTime.Now};

if ((NodeJsCourseStudents != null) && (NodeJsCourse != null))
{
using (TransactionScope scope = new TransactionScope())
{
try
{
//first try to delete student in course
DataContext.DummyTrails.Add(MyDummyTrail);
//DataContext.Course2Student.Remove(NodeJsCourse2Students);
DataContext.CourseToStudents.Remove(NodeJsCourseStudents);

//if succeed try to delete course

DataContext.Courses.Remove(NodeJsCourse);

DataContext.SaveChanges();
scope.Complete();
}
catch (Exception exp)
{
}
finally
{
scope.Dispose();
}
}
}
}
    When we comment out code as above no record is inserted into DummyTrail table neither the records from course,course2student or coursetostudent for node.js get deleted.Suppose there is no foreign key constraint then the delete will be simple as below

private static void DeleteNodeJs()
{
    //Data maping object to our database
    TestBedEntities DataContext = new TestBedEntities();
    Course NodeJsCourse = DataContext.Courses.FirstOrDefault(m => m.Name == "NODE js");

      if (NodeJsCourse != null)
     {
          DataContext.Courses.Remove(NodeJsCourse);
          DataContext.SaveChanges();
     }
}

Thanks & Happy Experimenting...