Caner Tosuner

Leave your code better than you found it

C# Custom Exception Oluşturma

Exception oluşturmak.. kulağa saçma geliyor dimi :) Heralde yazmış olduğumuz uygulamada bir exception meydana gelmesi en istemediğimiz şey dir. Ama bazen bilinçli olarak uygulamamızda exception fırlatmak isteyebiliriz (tabi handle edilmiş exception).

Exception uygulama çalışırken runtime da meydana gelen hatadır. Exception handling ise runtime da meydana gelen bu exception'ları yazılım tarafında ele alma tekniğidir diyebiliriz. Uygulamanızda bir exception meydana geldiğinde .Net kütüphanesinin direkt olarak fırlattığı stack trace açıklaması veya tuhaf exception mesajını kulanıcıya/client'a göndermek istemeyiz. Bu gibi durumlar için Custom Exception kullanımı devreye giriyor. Custom Exception kullanarak daha yönetilebilir ve daha anlamlı hata mesajları tanımlayabilir client'a dönebiliriz veya Log işlemlerinde handle ederken exception türüne göre daha farklı loglama işlemleri yapmak isteyebiliriz.

.Net tarafında bütün exception'ların base'i System.Exception class'ı dır ve custom tanımlanan exception class'larıda direkt veya dolaylı olarak bu class'tan inherit olmaktadır. Bizde yazacağımız custom exception class'ını System.Exception class'ından inherit edicez.

 

Custom Exception Tanımlama

Login işlemlerinde hata exception fırlatmak için kullanılacak LoginException adında bir custom exception tanımlayalım.

public class LoginException : System.Exception
    {
       //todo
    }

Şimdi uygulama içerisinde kullanımı için gerekli constructor tanımlamalarını yapalım.

   public class LoginException : System.Exception
    {
        public LoginException()
            : base()
        { }

        public LoginException(String message)
            : base(message)

        { }

        public LoginException(String message, Exception innerException)
            : base(message, innerException)
        { }

        protected LoginException(SerializationInfo info, StreamingContext context)
            : base(info, context)
        { }
    }

Yukarıda görüldüğü üzre LoginException'nın constructor'larını tanımaldık ve System.Exception class'ı için yapılacak constructor yönlendirmelerini yaptık, böylece LoginException class'ını kullanıma hazır hale getirdik.

 

Custom Exception Kullanımı

Yazmış olduğumuz LoginException'ı kullanırken aşağıdaki gibi try/catch içerisinde handle edip kullanabilir veya Postsharp Exception Handling, Logging yazısında da olduğu gibi üst veya ayrı katmanlarda exception'ı handle edebiliriz.

 void Login(string userName, string password)
        {
            try
            {
                if (userName != "canert" && password != "qwerty123")
                    throw new LoginException("Invalid login operation");
            }
            catch (LoginException loginException)
            {
                Console.WriteLine(loginException.Message);
            }
        }

İhtiyaç dahilinde çok daha farklı case'lerde kullanılmak üzre Custom Exception'lar tanımlayabilirsiniz. Exception fırlatmak düz mantıkla düşünüldüğünde tuhaf gelebilir ancak çoğu projelerde hayat kurtarır ve yazmış olduğunuz kodları tek bir yerden yönetebilme veya reusability gibi konuları projelerinizde uygulayabilmek için tercih edebilirsiniz ancak bu exception'ları handle etmeyi unutmamalıyız.

Repository Design Pattern

Çoğu yazılım uygulamasının belli mimari kurallar çerçevisinde farklı katmanlara ayrılarak geliştirilmesi beklenir ve buna n-tier architecture denir. N-tier architecture presentation tier, middle tier yada data tier gibi çeşitli katmanlardan oluşabilir. Presentation katmanında uygulamanızın UI tarafını ilgilendiren arayüzler bulunur, Middle katmanda business logic'i ilgilendiren geliştirmeler yapılır ve aynı zamanda database ile etkileşimde bu katmanda bulunur. Data katmanı ise uygulamanızdaki SQL Server, Oracle gibi database context'lerini içeren katmandır. Bu katman belkide uygulamadaki en önemli katmandır sebebi ise database. Data bizim için en önemli şey ve database'ler ile olan veri alış verişi işin en güvenlikli ve generic olması gereken yerlerin başında gelir.

Database varsa CRUD işlemi (Create, Read, Update, Delete) heralde olmazsa olmazdır ve data katmanı ile doğrudan business logic'in bulunduğu middle katman haberleşsin istenilmez ve bu durum bazen uygulamanızın büyüklüğüne göre çeşitli sorunlarda çıkartabilir. Repository pattern bu ihtiyacı karşılayan pattern olarak karşımıza çıkıyor ve temelde yaptığı iş ise business logic ile data access arasında arabulucu görevi görme.

Repository Pattern Faydaları

  • Maintainability (sonradan bakım kolaylılığı) arttırır,
  • Unit-test yapabilmemizi kolaylaştırır,
  • Esnek bir mimari uygulamamızı sağlar,
  • Yeni gelecek modüller veya istenilen değişiklikleri kolayca entegre edebilmeyi sağlar,
  • Domain driven development'ın önünü açar.

 

Repository Pattern Uygulaması

Şimdi örnek bir proje üzerinden Repository Pattern nasıl uygulanır görelim. Projeyi geliştirirken projenizde EntityFramework kurulu ve SampleDbContext adında bir database context'inizin olduğunu varsayarak ilerliyor olacağız.

1.Adım - IRepository adında CRUD işlemleri yapabilmemizi sağlayacak Generic bir interface metodlarını tanımlama.

public interface IRepository<T>  where T : class
    {
        IEnumerable<t> SelectAll();
        T SelectByID(object id);
        void Insert(T obj);
        void Update(T obj);
        void Delete(object id);
        void Save();
    }

2.Adım - BaseRepository adında Generic IRepository interface'inden implement olan class'ı oluşturma.

public abstract class BaseRepository<T> : IRepository<T> where T : class
    {
        private SampleDbContext db = null;
        private DbSet<t> table = null;
        public BaseRepository()
        {
            this.db = new SampleDbContext();
            table = db.Set<T>();
        }
        public BaseRepository(SampleDbContext db)
        {
            this.db = db;
            table = db.Set<T>();
        }
        public IEnumerable<T> SelectAll()
        {
            return table.ToList();
        }
        public T SelectByID(object id)
        {
            return table.Find(id);
        }
        public void Insert(T obj)
        {
            table.Add(obj);
        }
        public void Update(T obj)
        {
            table.Attach(obj);
            db.Entry(obj).State = EntityState.Modified;
        }
        public void Delete(object id)
        {
            T existing = table.Find(id);
            table.Remove(existing);
        }
        public void Save()
        {
            db.SaveChanges();
        }
    }

3.Adım - Employee adında örnek bir Entity tanımlama

public class Employee
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

 

4.Adım - BaseRepository den inherit olan Employee entity si için EmployeeRepository class'ını tanımlama

  public class EmployeeRepository : BaseRepository<Employee>
    {

    }

5.Adım - Yazmış olduğumuz EmployeeRepository'yi kullanmak

  void Main(string[] args)
        {
            var empRepository = new EmployeeRepository();
            empRepository.SelectAll();
        }

Projemiz hazır. Bundan sonra tanımlayacağınız entity'ler için EmployeeRepository de olduğu gibi Generic olarak yazmış olduğumuz BaseRepository'den türeyen Repository'ler tanımlayarak projeyi geliştirmeye devam edebiliriz.

Not : Bir rivayete göre Repository Design Pattern, Design Pattern ailesi içerisinde en çok kullanılan pattern olduğu söylenir.

 

PostSharp Kullanarak Metot Parametre Null Kontrolü

Daha önceki Aspect Oriented Programming'den ve Postsharp tool'undan bahsetmiştik. Postsharp ile Log, Exception Handling ve metot bazlı Authentication kontrolü gibi işlemleri yapabildiğimizden bahsetmiştik. Bu yazımızda ise Postsharp kullanarak metot bazlı parametre kontrolü (null mı değil i vs. gibi) nasıl yapabiliriz inceleyeceğiz.

Server side geliştirmesi yapmış arkadaşlar bilirlerki metotlarda gelen objeler için validation kontrolü yapmak bazen bizi çok yorabilir veya tahmin ettiğimizden daha fazla zaman aldırabilir. Postsharp ın OnEntry() metodunu kullarak gelen parametler için null mı değil mi diye kolayca kontrol edebiliriz.

Bunun için ilk olarak NullParamsControllingAspect adında OnMethodBoundaryAspect ten inherit olan class'ı oluşturalım ve içeriği aşağıdaki gibi olacaktır. Metoda yollanan null parametleri tespit edip string message olarak client'a ArgumentException fıraltarak bilgilendirme mesajını dönecektir.

[Serializable]
    public class NullParameterControllerAspect: OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionArgs args)
        {
            var messages = new StringBuilder();

            for (int i = 0; i < args.Arguments.Count; i++)
            {
                if (args.Arguments.GetArgument(i) == null)
                {
                    messages.AppendFormat(args.Method.Name +" metodunda bulunan \"{0}\" parametresi null olamaz.\n", args.Method.GetParameters()[i].Name);
                }
            }

            Console.WriteLine(messages);

            if (messages.Length > 0)
            {
                throw new ArgumentException(messages.ToString());
            }
        }

Yukarıda görüldüğü gibi OnEntry() metodunda gelen parametreyi alıp null mı diye kontrol ediyoruz.

Test için aşağıdaki gibi Prgram.cs class'ını oluşturalım. TransferRequest adında bir class'ımız olsun ve bu class'ı null set ederek ve MoneyTransfer metodunu çağıralım.

 class Program
    {
        static void Main(string[] args)
        {
            TransferRequest req_null = null;
            MoneyTransfer(req_null, "Test");//null obje kullanarak metot çağırımı
        }

        [NullParameterControllerAspect]
        public static void MoneyTransfer(TransferRequest request, string param)
        {
            //transfer işlemleri
        }

        public class TransferRequest
        {
            public decimal Amount { get; set; }
            public string SenderIBAN { get; set; }
            public string ReceiverIBAN { get; set; }
        }
    }

 Projenizi çalıştırdığınızda ekran çıktısı aşağıdaki gibi olacaktır.