SOLID Principles-O-Open Closed Principle

“software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification

Untuk mengimplement OCP, sebelumnya kode kita harus SRP – lihat pada tulisan SOLID Prinsiples – S-Single Responsibility, sehingga kita dapat mengindentifikasi dan memisahkan behavior yang berubah kedalam berbagai class. Output dari OCP adalah sekumpulan class yang memiliki behavior yang sama tapi dengan implementasi yang berbeda-beda. Perhatikan potongan kode program di bawah ini.

public class DiscountService
{
private const int GOLD = 1;
private const int SILVER = 2;
private double _totalSales;
public DiscountService(double TotalSales)
{
_totalSales = TotalSales;
}

private int _custType;
public int CustType
{
get { return _custType; }
set { _custType = value; }
}

public double GetDiscount(int CustTpe)
{
if (_custType == GOLD)
{
return _totalSales - 100;
}
else if (_custType == SILVER)
{
return _totalSales - 50;
}
return _totalSales;
}
}

Masalah timbul ketika kita akan menambahkan beberapa tipe customer lain, maka akan sangat banyak kondisi “IF” di dalam kode program. Menambahkan kondisi “IF” baru artinya kita mengubah class CustomerService dan itu melanggar prinsip Close for modification.

Lantas bagaimana caranya mengimplementasikan prinsip Open for extension? Yaitu dengan menggunakan inheritance (lewat parent classnya) atau abstraksi (bisa menggunakan abstract class ataupun interface).

public interface ICustomer
{
string Name { get; }
double GetDiscount(double TotalSales);
}

Kemudian kita buat beberapa class konkrit untuk implementasi interface ICustomer di atas.

public class GoldCustomer : ICustomer
{
public string Name
{
get
{
return "Gold";
}
}

public double GetDiscount(double TotalSales)
{
return TotalSales - 100;
}
}
public class SilverCustomer : ICustomer
{
public string Name
{
get
{
return "Silver";
}
}

public double GetDiscount(double TotalSales)
{
return TotalSales - 50;
}
}

Kemudian mari kita refactor kode program class DiscountService di atas sehingga jika ingin menambah tipe customer lagi, tidak perlu mengubah implementasi dari class DiscountService.

public class DiscountService
{
private readonly ICustomer _discount;
private double _totalSales;
public DiscountService(double TotalSales, ICustomer discount)
{
_totalSales = TotalSales;
_discount = discount;
}
public virtual double GetDiscount()
{
return _discount.GetDiscount(_totalSales);
}
}

class Program
{
static void Main(string[] args)
{
double totalSales = 1000;
DiscountService discountService;
var listCustomer = new List();
listCustomer.Add(new GoldCustomer());
listCustomer.Add(new SilverCustomer());
foreach (var customer in listCustomer)
{
discountService = new DiscountService(totalSales, customer);
Console.WriteLine("Customer Type: " + customer.Name);
Console.WriteLine("Total after discount : " + discountService.GetDiscount());
Console.WriteLine("----------------------");
}

Console.ReadKey();

}
}

ocp

Sekarang kita bisa menambahkan 1 jenis customer lagi, tanpa harus mengubah code pada class Discount Service.

listCustomer.Add(new PlatinumCustomer());

ocp-2.png

SOLID Prinsiples – S-Single Responsibility

S.O.L.I.D prinsip adalah kumpulan dari best-practices untuk Object Oriented design. Semua design pattern pada Gang Of Four juga dibangun berdasarkan prinsip ini.

SOLID merupakan  prinsip dasar dalam membangun arsitektur software yang baik. SOLID merupakan singkatan dari 5 prinsip:

  1. S dari SRP (Single Responsibility Principle)
  2. O dari OCP (Open Closed Principle)
  3. L dari LSV (Liskov Substitution Principle)
  4. I dari ISP (Interface segregation principle)
  5. D dari DIP ( Dependency Inversion Principle)

S-Single Responsibility Principle

“A class should have only one reason to change.” – Robert C Martin

Sebuah class seharusnya diubah itu satu alasan aja. Terlalu banyak tanggung jawab yang dibebankan pada sebuah class sehingga membuat satu perubahan kode program bisa jadi menimbulkan banyak efek kepada yang lain. Fokus pada satu tanggung jawab memudahkan maintenance code.

public class Customer
{
public void Add()
{
try
{
// Database code goes here
}
catch (Exception ex)
{
System.IO.File.WriteAllText(@"c:\Error.txt", ex.ToString());
}
}
public void Delete()
{
try
{
// Database code goes here
}
catch (Exception ex)
{
System.IO.File.WriteAllText(@"c:\Error.txt", ex.ToString());
}
}
}

Pada class Customer method Add dan Deleta harusnya hanya menghandle satu high level abstraksi. Di dalam class tersebut juga terdapat duplikasi kode program penulisan log.
Penulisan log harusnya ditangani oleh class lain dalam hal ini class FileLogger. Sehingga jika ada perubahan pada proses penulisan log seperti path file yang diubah atau bahkan penulisan log dipindahkan ke database, kita bisa fokus hanya pada class FileLogger saja. Ingat! SRP bukan berarti satu class hanya boleh memiliki satu method saja, tapi hanya butuh satu alasan untuk memodifikasi class tersebut.

public class Customer
    {

        FileLogger _logger = new FileLogger();
        public void Add()
        {
            try
            {
                // Database code goes here
            }
            catch (Exception ex)
            {
                _logger.WriteLog(ex.Message);
            }
        }
        public void Delete()
        {
            try
            {
                // Database code goes here
            }
            catch (Exception ex)
            {
                _logger.WriteLog(ex.Message);
            }
        }
    }

    public class FileLogger
    {
        public void WriteLog(string message)
        {
            System.IO.File.WriteAllText(@"c:\Error.txt", message);

        }

Apa manfaat dari SRP?
1. Reuse, lebih mudah untuk mendaya-guna kode program
2. Clarity, kode yang kita menjadi sangat jelas karena ditulis hanya focus pada satu hal
3. Naming, tentu dengan hanya berfokus pada satu hal, penamaan class jadi lebih gampang.
4. Readability, karena berkaitan dengan Clarity dan Naming, maka menjadi sangat membantu dalam pembacaan kode program
5. Reduced Coupling, menurunkan tingkat kebergantungan antar class. Semakin tinggi kebergantungan, semakin susah untuk dimodifikasi

Custom Configuration Handler

File configurasi baik itu app.config maupun web.config adalah file xml yang berisi configurasi external dari aplikasi yang kita buat. Seringkali kita melakukan pemanggilan nilai pada file configurasi seperti ini.

var baseUrl = ConfigurationManager.AppSettings["BaseUrl"].ToString();

Semakin kompleks aplikasi, terkadang membuat bertambahnya nilai yang perlu di store di configurasi. Supaya configurasi kita mudah di maintenance jika ada perubahan key pada configurasi dan menghindari kesalahan pengetikan pada key, kita bisa membuat class untuk menghandle issue tersebut.

public class BLLConfiguration : ConfigurationSection
{
public string BaseUrl { get; set; }
public static BLLConfiguration GetConfig()
{
var section = ConfigurationManager.GetSection("Astral/BusinessLogic");
if (section == null)
throw new ConfigurationErrorsException(
ErrorMessages.ConfigurationErrorsException);

return (BLLConfiguration)ConfigurationManager.GetSection("Astral/BusinessLogic");
}

public void LoadConfigValues(XmlNode node)
{
var attributeCollection = node.Attributes;
if (attributeCollection["BaseUrl"] != null)
BaseUrl = attributeCollection["BaseUrl"].Value;
}
}

public class BLLConfigurationHandler : IConfigurationSectionHandler
{
public Object Create(Object parent, Object configContext, XmlNode node)
{
var config = new BLLConfiguration();
config.LoadConfigValues(node);
return config;
}
}

Pada web config/appconfig daftarkan section tersebut. Pada type, formatnya adalah {Namespace.ClassName},{AssemblyName}

custom_config.png

Kemudian, assign value pada confurasi dibuat dalam satu section berdasarkan pada nama sectionGroup dan section-nya.

custom_config_1.png

Pemanggilan value dari configurasi jadinya seperti ini.

var baseUrl = BLL.BLLConfiguration.GetConfig().BaseUrl;

Mocking Unit Test Object Menggunakan NSubtitute

NSubtitute

http://nsubstitute.github.io/

Nsubtitute adalah salah satu mocking library untuk .NET yang memiliki configurasi yang sederhana sehingga mudah digunakan. NSubtitute di design untuk testing dengan pendekatan Arrange-Act-Assert (AAA), sehingga kita hanya butuh untuk mengatur bagaimana harusnya dia bekerja dan kemudian membuat pemanggilan Assertion.

Kita bisa menginstall NSubtitute lewat Packet Manager Console pada Visual Studio.

install-nsubtitute.png

Sebagai contoh kita akan membuat abstraksi operasi pada Calculator menggunakan interface.

public interface ICalculator
{
   int Add(int number1, int number2);
}

Kemudian kita mempunyai sebuah class yang menyediakan service untuk menampilkan hasil kalkulasi tersebut. Prinsip dasar unit test adalah melakukan test pada unit terkecil dari kode program kita dan mengisolasi extenal class yang ada.

Karena kita akan membuat unit test untuk class CalculatorService, sehingga external class berupa class implentasi dari ICalculator kita buatkan mockup nya saja.

public class CalculatorService
   {
       private ICalculator _calculator;
       public CalculatorService(ICalculator calculator)
       {
           _calculator = calculator;
       }
       public int Add(int number1, int number2)
       {
           if (number2 == 0)
           {
               throw new Exception("Dived by Zero Exception, please input number2 != 0");
           }
           var result = _calculator.Add(number1, number2);
           return result;
       }
   }

Setiap class Unit Test haruslah ditandai dengan attribute [TestClass] dan Unit Test Method dengan attribute [TestMethod].

[TestClass]
public class CalculatorServiceTest
{
    [TestMethod]
    public void Add_ReturnValidResult()
    {
    }
}

Jika kita menginginkan suatu proses tertentu yang harus dilakukan sebelum unit test method dijalankan seperti proses inisialisasi data, kita bisa membuat method dengan attribute [TestInitialize] dan sebalikanya jika kita menginginkan sebuah proses yang dieksekusi setiap sebuah unit test method di jalankan kita bisa membuta method dengan attribute [TestCleanup].

[TestInitialize]
public void Setup()
{

}
[TestCleanup]
public void Cleanup()
{
}

Jika Kita melakukan test terhadap expection yang mungkin ada, tambahkan attribute [ExpectedException].

[TestMethod]
[ExpectedException(typeof(Exception))]
public void Add_WhenNumber2IsZeroReturnException()
{
}

Untuk melakukan mockup object, cukup dengan menulis kode seperti ini. Usahakan implementasi yang kita buat selalu berawal dari abstraksi sehingga memudahkan menulis kode mockup objectnya.
csharp _calculator = Substitute.For();

Selanjutnya kita akan memockup method Add pada ICalculator seperti ini.
csharp _calculator.Add(1, 2).Returns(3);

Bentuk class unit test selengkapnya sepert ini.

[TestClass]
    public class CalculatorServiceTest
    {
        private ICalculator _calculator;
        private CalculatorService _calculatorService;

        [TestInitialize]
        public void Setup()
        {
            _calculator = Substitute.For<ICalculator>();
            _calculatorService = new CalculatorService(_calculator);
        }
        [TestCleanup]
        public void Cleanup()
        {
            _calculator = null;
            _calculatorService = null;
        }

        [TestMethod]
        public void Add_ReturnValidResult()
        {
            var expectedResult = 3;
            ///arrange
            _calculator.Add(1, 2).Returns(expectedResult);
            ///act
            var actualResult = _calculatorService.Add(1, 2);
            ///assert
            Assert.AreEqual(expectedResult, actualResult);
        }
        [TestMethod]
        [ExpectedException(typeof(Exception))]
        public void Add_WhenNumber2IsZeroReturnException()
        {
            try
            {
                _calculatorService.Add(1, 0);
                Assert.Fail();
            }
            catch(Exception ex)
            {
                Assert.IsInstanceOfType(ex, typeof(Exception));
                Assert.AreEqual("Dived by Zero Exception, please input number2 != 0", ex.Message);
                throw ex;

            }
           
        }
    }

 

Mari kita jalankan class unit test kita.

pass-unit-test

Query menggunakan kondisi selisih DateTime pada Entity Framework

Tidak semua query pada Linq di support oleh Entity Framework, termasuk pengurangan 2 nilai datetime.

public List<Price> GetExpiredPrice()
{
   return _priceRepo.Get(x=>x.EndDate.Value.Subtract(DateTime.Now).Days == Core.Constants.PriceReminderDays).ToList();
}

Query di atas akan menghasilkan error

cannot be converted to SQL.

Gunakan DbFunctions dari  assembly System.Data.Entity

public List<Price> GetExpiredPrice()
{
    return _priceRepo.Get(x=>DbFunctions.DiffDays(DateTime.Now, x.EndDate) == Core.Constants.PriceReminderDays).ToList();
}

 

 

Perbedaan Asosiasi, Agregasi dan Komposisi pada Object Oriented Programming

Ketika kita membaca atau membuat diagram class UML, kita tidak pernah lepas dari relasi antar class. Seringkali membingungkan adalah bagaimana implementasi relasi tersebut dalam bentuk kode program.

Ada beberapa teknik yang digunakan jika berbicara mengenai relasi antar class yaitu:

Assosiation (Assosiasi)

Assosiasi adalah hubungan antara object. Relasi assosiasi biasa disebut is “a” relationship. Assosiasi berarti bahwa sebuah object “menggunakan” object yang lain. Assosiasi adalah sebuah relasi dimana semua object memiliki lifecycle nya sendiri dan tidak ada yang bertindak sebagai owner.

Contoh Teacher dan Student. Banyak Student dapat berasosiasi dengan satu Teacher dan satu Student   dapat berasosiasi dengan banyak Teacher. Object keduanya bisa dibuat dan dihancurkan secara mandiri tanpa berpengaruh pada eksistensi object lain.

Aggregation (Agregasi)

Agregasi adalah bentuk hubungan yang lebih khusus dari Assosiasi dimana sebuah object memiliki lifecycle nya sendiri tapi dengan kepemilikan dan class child tidak dapat memiliki class parentnya. Relasinya biasa di sebut relasi “has-a”.

Sebagai contoh, Department dan Teacher. Satu Teacher tidak dapat memiliki banyak Department, tapi jika kita menghapus object Department, object Teacher tidak akan ikut terhapus. Contoh:

public class Department
    {
        private string _name;

        public Department(string name)
        {
            _name = name;
            _teachers = new List<Teacher>();
        }
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        public List<Teacher> _teachers;
    }

 

public class Teacher
    {
        private string _name;
        public Teacher(string name)
        {
            _name = name;
        }
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
    }

 

public class ClientTester
    {
        public void AggregationTest()
        {
            var department = new Department("Electrical Engineering");

            var teacher1 = new Teacher("Teacher 1");
            var teacher2 = new Teacher("Teacher 2");

            department._teachers.Add(teacher1);
            department._teachers.Add(teacher2);

        }
    }

Karena object teacher di buat di luar object department, sehingga jika object department dihancurkan maka object teacher tetap exist. Object department bisa memiliki banyak object teacher yang berasosiasi dengannya, tapi tidak sebaliknya. Sehingga dalam relasi ini object department adalah owner dari object teacher.

Relasi Agregasi disebut juga Weak Assosiation dan dalam UML disimbolkan seperti ini

agregasi

 

Composition (Komposisi)

Komposisi sama dengan relasi Agregasi, tapi lebih spesfic lagi yaitu lifecycle object bergantung pada object ownernya.

public class University
    {
        private string _name;
        private List<Department> _departments;
        public University(string name)
        {
            _name = name;
        }

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        private void GenerateDepartment()
        {
            var electricalDepartment = new Department("Electrical Engineering");
            var biologyDepartment = new Department("Biology");
            _departments = new List<Department>
                {
                    electricalDepartment,
                    biologyDepartment
                };

        }
    }

 

public class ClientTester
{
  public void CompositionTest()
  {
      var university = new University("Diponegoro University");

   }

 }

Pada contoh ini class University bertindak sebagai object owner seperti pada relasi Agregasi di atas. Karena object electricalDepartment dan biologyDepartment di buat di dalam object univerty sehingga jika object university dihancurkan, maka object electricalDepartment dan biologyDepartment akan tidak exist lagi. Relasi Komposisi disebut juga Strong Association atau Death Relationship dan disimbolkan dalam UML seperti ini

komposisi

 

 

 

Interface, penting tapi sering diabaikan

Beberapa kali saya melakukan interview untuk kandidat programmer di kantor, seringkali untuk pertanyaan paling dasar mengenai Object Oriented Progamming adalah pertanyaan apa itu Interface? sebagian candidat biasanya menjawab pernah menggunakan tapi tidak bisa mengungkapkan dalam secara verbal. Bahkan ada yang menjawab bahwa itu adalah antarmuka (hmm, mungkin dikiranya User Interface).

Walaupun ada teman yang menganalogikan interface adalah template, tapi bagi saya analogi interface yang paling mendekati adalah bahwa interface itu semacam contract yang mengikat semua child class (semua class yang mengimplementasikan interface tersebut).

Di dalam sebuah interface bisa jadi berisi sekumpulan method dan property. Ingat, sebuah interface tidak bisa memiliki implementasi di dalamnya, berbeda dengan abstract class. Dengan menggunakan interface, memudahkan kita membuat mockup object dalam unit test misalnya. Terutama jika kita menggunakan IoC container. Interface ini adalah salah satu feature dalam OOP yang digunakan untuk prinsip polymorphisme.

Contoh:

interface IFlyObject
        {
            void Fly();
        }

Selanjutnya kita buat class Bird dan Plane yang akan mengimplementasi IFlyObject.

interface

Akan ada muncul compiler error jika interface member dari IFlyObject yaitu Fly() tidak di implementasikan. Mari kita modifikasi class di atas.

public class Bird : IFlyingObject
        {
            public void Fly()
            {
                Console.WriteLine("Terbang dengan Sayap Bulu");
            }
        }

        public class Plane : IFlyingObject
        {
            public void Fly()
            {
                Console.WriteLine("Terbang dengan mesin jet");
            }
        }

Salah satu prinsip OOP adalah Polymorphisme. Polymorphisme artinya kita memproses object-object yang berbeda berdasarkan tipe datanya. Dengan kata lain sebuah method yang memiliki implementasi berbeda-beda, implementasinya dapat dipilih saat runtime bergantung pada kondisi tertentu. Dari Polymorphisme kita mendapatkan keuntungan berupa flexsibilitas dalam memilih class implementasi.

static void Main(string[] args)
        {
            Console.Write("Pilih (A) Bird (B) Plane : ");
            var input = Console.ReadLine();
            IFlyingObject flyObject;
            if (input.ToUpper() == "A")
            {
                flyObject = new Bird();

            }
            else if (input.ToUpper() == "B")
            {
                flyObject = new Plane();

            }
            else
            {
                throw new Exception("Pilih A atau B");
            }
            flyObject.Fly();
            Console.ReadKey();
        }

Lebih jelas penggunaanya nanti kita akan bahas pada tulisan mengenai SOLID Pattern

Generic Repository Entity Framework Part II

lanjutan dari Generic Repository Entity Framework Part I

Unit Of Work

Unit Of Work memastikan bahwa banyak repository hanya akan menshare satu database contex yang sama sehingga membuat transaksi menjadi atomic. Artinya jika satu gagal, semuanya gagal untuk menjaga konsistensi data.

Seperti biasa, kita buat interfacenya dulu.

public interface IUnitOfWork : IDisposable
     {
         IGenericRepository GetGenericRepository()
           where T : class;

         void SaveChanges();
     }

Kemudian kita membuat konkrit classnya.

public class EFUnitOfWork : IUnitOfWork
     {
         private BookStoreEntities _context = new BookStoreEntities();
         private bool _disposed;



         /// <summary>
         /// Gets the generic repository.
         /// </summary>
         /// <typeparam name="T"></typeparam>
         /// <returns></returns>
         public IGenericRepository<T> GetGenericRepository<T>() where T : class
         {
             return new EFGenericRepository<T>(_context);
         }


         /// <summary>
         /// Saves the changes.
         /// </summary>
         public void SaveChanges()
         {
             try
             {
                 _context.SaveChanges();
             }
             catch (DbEntityValidationException e)
             {
                 throw;
             }
         }

         /// <summary>
         /// Releases unmanaged and - optionally - managed resources.
         /// the dispose method is called automatically by the injector depending on the lifestyle
         /// </summary>
         /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
         protected virtual void Dispose(bool disposing)
         {
             if (!this._disposed)
             {
                 if (disposing)
                 {
                     _context.Dispose();
                 }
             }
             this._disposed = true;
         }

         public void Dispose()
         {
             Dispose(true);
             GC.SuppressFinalize(this);
         }
     }

 

Nantinya kita akan membuat instance dari Generic Repository di atas dengan melewatkan class entities sebagai paramater dan menggunakan method GetGenericRepository

public class BookBLL : IBookBLL
        {
            private readonly IUnitOfWork _unitOfWork;
            private readonly IGenericRepository _bookRepo;
            public BookBLL(IUnitOfWork unitOfWork)
            {
                _unitOfWork = unitOfWork;
                _bookRepo = _unitOfWork.GetGenericRepository();
            }

Generic Repository Entity Framework Part I

Generic mengacau pada teknik menulis kode untuk sebuah class tanpa menentukan tipe datanya. Dengan menggunakan Generic class kita tidak perlu menulis implementasi repository untuk semua class yang ada. Intinya adalah memaksimalkan code reuse.

C# juga menyediakan feature pada generic untuk membatasi tipe apa saja yang bisa menjadi parameter class, sebagai contoh parameter T pada interface IGenericRepository dibatasi pada tipe class saja.

Pertama kita akan buat interface, ya usahakan selalu membuat abstraksi sebelum membuat implementasi class untuk memudahkan unit test dan mengurangi tingkat depency antar class.

public interface IGenericRepository where TEntity : class
        {
            IEnumerable Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable, System.Linq.IOrderedQueryable> orderBy = null, string includeProperties = "");

            TEntity GetById(object id);

            TEntity GetById(params object[] keyValues);

            void Insert(TEntity entity);

            void Update(TEntity entityToUpdate);

            void InsertOrUpdate(TEntity entity);

            void Delete(object id);

            void Delete(TEntity entityToDelete);

            bool Exists(TEntity entity);

            long Count(Expression<Func<TEntity, bool>> filter = null);
        }

Selanjutnya kita buat class konkrit untuk implentasi dari IGenericRepository ini. Kita menggunakan Entity Framework. Jika kita menggunakan provider lain buat implementasi yang berbeda.

public class EFGenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
     {
         internal BookStoreEntities Context;
         internal DbSet<TEntity> Entities;

         public EFGenericRepository(BookStoreEntities dbContext)
         {
             Context = dbContext;
             Entities = dbContext.Set<TEntity>();
         }

         /// <summary>
         /// Gets the specified filter.
         /// </summary>
         /// <param name="filter">The filter.</param>
         /// <param name="orderBy">The order by.</param>
         /// <param name="includeProperties">The include properties.</param>
         /// <returns></returns>
         public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
         {
             IQueryable<TEntity> query = Entities;

             if (filter != null)
             {
                 query = query.Where(filter);
             }

             foreach (var includeProperty in includeProperties.Split
                 (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
             {
                 query = query.Include(includeProperty);
             }

             if (orderBy != null)
             {
                 return orderBy(query).ToList();
             }

             return query.ToList();
         }

         /// <summary>
         /// Get entity the by identifier.
         /// </summary>
         /// <param name="id">The identifier.</param>
         /// <returns></returns>
         public TEntity GetById(object id)
         {
             return Entities.Find(id);
         }

         /// <summary>
         /// Get entity by identifiers.
         /// </summary>
         /// <param name="keyValues">The key values.</param>
         /// <returns></returns>
         public TEntity GetById(params object[] keyValues)
         {
             return Entities.Find(keyValues);
         }

         /// <summary>
         /// Inserts the specified entity.
         /// </summary>
         /// <param name="entity">The entity.</param>
         public void Insert(TEntity entity)
         {
             Entities.Add(entity);
         }

         /// <summary>
         /// Updates the specified entity.
         /// </summary>
         /// <param name="entityToUpdate">The entity to update.</param>
         public void Update(TEntity entityToUpdate)
         {
             Context.Entry(entityToUpdate).State = EntityState.Modified;
         }

         /// <summary>
         /// Inserts the entity if not exist or update the entity if exist.
         /// </summary>
         /// <param name="entity">The entity.</param>
         public void InsertOrUpdate(TEntity entity)
         {
             if (!Exists(entity))
                 Insert(entity);
             else
                 Update(entity);
         }

         /// <summary>
         /// Deletes entity by identifier.
         /// </summary>
         /// <param name="id">The identifier.</param>
         public void Delete(object id)
         {
             var entityToDelete = Entities.Find(id);
             Entities.Remove(entityToDelete);
         }

         /// <summary>
         /// Deletes the specified entity.
         /// </summary>
         /// <param name="entityToDelete">The entity to delete.</param>
         public void Delete(TEntity entityToDelete)
         {
             if (Context.Entry(entityToDelete).State == EntityState.Detached)
             {
                 Entities.Attach(entityToDelete);
             }
             Entities.Remove(entityToDelete);
         }

         /// <summary>
         /// Check if the entity exist.
         /// </summary>
         /// <param name="entity">The entity.</param>
         /// <returns></returns>
         public bool Exists(TEntity entity)
         {
             var objContext = ((IObjectContextAdapter)Context).ObjectContext;
             var objSet = objContext.CreateObjectSet<TEntity>();
             var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entity);

             Object foundEntity;
             var exists = objContext.TryGetObjectByKey(entityKey, out foundEntity);
             return (exists);
         }

         /// <summary>
         /// Counts the specified filter.
         /// </summary>
         /// <param name="filter">The filter.</param>
         /// <returns></returns>
         public long Count(Expression<Func<TEntity, bool>> filter = null)
         {
             IQueryable<TEntity> query = Entities;

             if (filter != null)
             {
                 query = query.Where(filter);
             }
             return query.Count();
         }
     }

 

Untuk jaminan proses transaksi database atomic (satu gagal, gagal semua). Kita akan membuat class UnitOfWork yang bertanggung jawab menangani proses transaksi di tulisan yang lain.

Func dan Expression C#

Perhatikan senarai kode program di bawah ini.

Expression<Func<TEntity, bool>> filter

Func adalah sebuah generic delegate – delegate adalah method yang di lewatkan sebagai parameter dalam  method yang lain. Func tersebut memiliki parameter TEntity dan nilai kembali bertipe bool.  Contoh:

func

Sedangkan Expression<Func> menghasilkan tree data struktur untuk lamba expression. Karena kembalian Func adalah bool, kita bisa gunakan expression tersebut sebagai Lamba Expression pada Linq .Where

exp