Sayfalar

9 Mart 2015 Pazartesi

Tasarım Desenleri-3

Tasarım Desenleri-3

Proxy Pattern İle İzin Yönetimi

Bu blogta transparent proxy ile method bazlı yetkilendirmenin nasıl yapılabileceğini anlatacağım. Öncelikle problemimizden bahsetmek istiyorum daha sonrada çözüm yollarını araştıracağız. İş katmanında yazılan kodlara yetki kontrolü eklemek istiyorum. Bunun için her metoda tek tek girip sen şu roller için çalışacaksın demek istemiyorum. Fakat methodların hangi rollerde olduğuna dair bilgi üzerindeki "attributeler" de mevcuttur. Benim yapmam gereken bu methodlar çağrılmadan önce yetki kontrolü yapmak, eğer yetkisiz erişim mevcutsa hata dönderip methodu çalıştırmamak. Yetki kontrolünü tek bir merkezden yapmak istiyorum. Kısacası  iş katmanı sınıflarındaki methodların önüne geçip yetki kontrolu yapmak bizim problemimiz.

Bu problemi çözmek için reflection kullanılabilir, fakat zaten bunu bizim yerimize yapan bir sınıf var. RealProxy sınıfı MarshalByRefObject sınıfından miras alınarak oluşturulmuş sınıfları açabiliyor ve bize gerekli kodlamayı eklememize izin veriyor. Sonunda o sınıf gibi fakat kodlarımızın eklendiği bir sınıf dönderebiliyoruz. Buna microsoft TransparentProxy adını koymuş.

Öncelikle herhangi bir sınıfı içine alıp TransparentProxy (aynı sınıfın düzenlenmişini) sini dönderen generic bir method yazalım.

public class GenericProxyBuilder<TProxyObj> : RealProxy where TProxyObj : new()
    {
        TProxyObj proxyObj;
        public GenericProxyBuilder(TProxyObj proxyObj)
            : base(typeof(TProxyObj))
        {
            this.proxyObj = proxyObj;
        }
        public GenericProxyBuilder() { }
        public static TProxyObj GetInstance()
        {
            return (TProxyObj)new GenericProxyBuilder<TProxyObj>(new TProxyObj()).GetTransparentProxy();
        }
        public override IMessage Invoke(IMessage msg)
        {
            IMethodCallMessage mcm = (IMethodCallMessage)msg;
            IMessage rtnMsg = null;
            if (mcm != null)
            {
                rtnMsg = new ReturnMessage(mcm.MethodBase.Invoke(proxyObj, mcm.InArgs), null, 0, mcm.LogicalCallContext, mcm);
            }
            return rtnMsg;
        }

    }

Bu sınıf tip olarak içine aldığı sınıftan TransparentProxy oluşturuyor. Abstract olan Invoke metodu ile o sınıfa ait tüm methodların arasına girmiş olacağız. Şimdi de örnek bir iş katmanı sınıfı yazalım. Bu sınıf  MarshalByRefObject  sınıfından miras almak zorundadır.

public  class BussinessLayerCodes:MarshalByRefObject
    {
       public void Method1()
       {
       }
    }

Şimdi de bu kodu çalıştıralım.

class Program
    {
        static void Main()
        {
            var instance = GenericProxyBuilder<BussinessLayerCodes>.GetInstance();
            instance.Method1( );
        }
    }


Bu kodu çalıştırdığımızda "Araya girdik." çıktısından sonra kodumuzun çalıştığını göreceksiniz. Şimdi bu özelliği izin yönetiminide nasıl kullanacağız onu gösterecem.
Öncelikle bir tane attribute tanımlayıp Method1'in üzerine koyup bu methodu işaretleyelim.


  class RoleAttribute:Attribute
  {
      public string Role { get; set; }

  }

public  class BussinessLayerCodes:MarshalByRefObject
    {
       [PermissionAttribute(Role="Manager")]
       public void Method1()
       {
       }
    }

Bizim GenericProxyBuilder sınıfımızı biraz düzenleyerek proxy yapılmış sınıfın metodunun öncesinde ve sonrasında çalışacak methodları parametrik yapalım.

    public class GenericProxyBuilder<TProxyObj> : RealProxy where TProxyObj : new()
    {
        TProxyObj proxyObj;
        public GenericProxyBuilder(TProxyObj proxyObj)
            : base(typeof(TProxyObj))
        {
            this.proxyObj = proxyObj;
        }
        public GenericProxyCreator() { }
        public static TProxyObj GetInstance(Action<MethodBase> after = null, Action<MethodBase> before=null)
        {
            return (TProxyObj)new GenericProxyBuilder<TProxyObj>(new TProxyObj()) { Before = before ?? delegate { }, After = after ?? delegate { } }.GetTransparentProxy();
        }
        private Action<MethodBase> After { get; set; }
        private Action<MethodBase> Before { get; set; }
        public override IMessage Invoke(IMessage msg)
        {
            IMethodCallMessage mcm = (IMethodCallMessage)msg;
            IMessage rtnMsg = null;
            if (mcm != null)
            {
                Before(mcm.MethodBase);
                rtnMsg = new ReturnMessage(mcm.MethodBase.Invoke(proxyObj, mcm.InArgs), null, 0, mcm.LogicalCallContext, mcm);
                After(mcm.MethodBase);
            }
            return rtnMsg;
        }
    }

Daha sonrada transparen proxy ile iş katmanı sınıflarını haberleştirecek generic bir sınıf yazalım.

    public class BussinessCodeCaller<BussinessLayerObject> where BussinessLayerObject : new()
    {
        BussinessLayerObject bussinessObj;
        public BussinessCodeCaller(BussinessLayerObject bussinessObj)
        {
            this.bussinessObj = bussinessObj;
        }
     
        static String[] DemoAuthenticatedUserRoles = { "Manager", "Director" };
        public static void ControlRoles(MethodBase mcm)
        {
            var HasRole = mcm.GetCustomAttributes(true).Where(x => x is RoleAttribute && DemoAuthenticatedUserRoles.Contains(((RoleAttribute)x).Role)).Any();
            if (HasRole == false)
                throw new Exception("Authentication failure!");
        }
        public static void Logging(MethodBase mb)
        {
            //loglama yapılabilir.
        }
        public static BussinessLayerObject Instance
        {
            get
            {

                return GenericProxyBuilder<BussinessLayerObject>.GetInstance(before:ControlRoles,after:Logging);
            }
        }
    }

Yazdığımız kodları test edelim. Aşagıdaki gibi çağıralım. 

  class Program
    {
        static void Main()
        {
         
            var instance = BussinessCodeCaller<BussinessLayerCodes>.Instance;
            instance.Method1();
        }
    }

Yetki hatası vermeyecektir. Şimdide Method1 etiket değerini [PermissionAttribute(Role="Manager")] yerine [PermissionAttribute(Role="Personnel")] ile değiştirelim. Tekrar test ettiğimizde yetki hatasının verdiği görülecektir.





10 Şubat 2015 Salı

Tasarım Desenleri-2

Tasarım Desenleri-2 (Collection Pattern)

Bu desen benim tarafımdan geliştirildi. Eğer bir nesnenin özelliği (örnekte özellik "id") kolleksiyondaki nesnelerden yatay (aynı listede) ve dikey (chield-parent ilişkisi) etkileniyorsa bu desen kullanılabilir. Bunun dışında "Parent-Chield" ilişkisi olan modellerde bu ilişkinin doğal yoldan oluşmasını sağlayabilirsiniz yani nesnenin ParentObject veya ChieldObject diye bir parametresi varsa bunları belirtmenizi gereksiz kılar.(Nesne eklenme esnasında bu belirtimleri kendisi yapar.) Hiyerarşik controllerde id tanımlamasında güçlük çekilebilmektedir. Bu örnekte "id" ataması kontrolün eklenmesi ile oluşturuluyor.

using System;
using System.Collections.Generic;

namespace ConsoleApplication1.CreationalPatterns
{
    class MainProgram
    {
        public static void Main()
        {

            FormControl AnaForm = new FormControl() { Id = "AnaForm" };
            FormControl AraForm = new FormControl();
            AnaForm.Controls.Add(AraForm);

            AraForm.Controls.Add(new Button() { Text = "Btn1" });
            AraForm.Controls.Add(new Button() { Text = "Btn2" });

            Console.WriteLine("Ana form kontrolleri");
            AnaForm.Controls.ForEach(x => Console.WriteLine(x.Id));

            Console.WriteLine("Ara form kontrolleri");
            AraForm.Controls.ForEach(x => Console.WriteLine(x.Id));

            Console.ReadKey();
        }
    }
    public class Field
    {
        public object Id { get; set; }
        public string Name { get; set; }
    }
    public class BaseControl : Field
    {
        public BaseControl(){}
        public IdMode IdMode { get; set; }
    }
    public enum IdMode { Dynamic, Static }

    public class ControlCollection : List<BaseControl>
    {
        public FormControl control;
        public ControlCollection(FormControl control)
        {
            this.control = control;
        }
        public ControlCollection() { }
        public new void Add(BaseControl dtn)
        {
            if (dtn.IdMode == IdMode.Dynamic)
            {
                dtn.Id = control.Id + "_" + this.Count;
                dtn.IdMode = IdMode.Static;
            }
            base.Add(dtn);
        }
    }
    public class FormControl : BaseControl
    {
        public int DynamicTemplateId { get; set; }
        public FormControl()
        {
            Controls = new ControlCollection(this);
        }
        public ControlCollection Controls { get; protected set; }
    }
    public class Button : BaseControl
    {
        public Button(){       }
        public string Text { get; set; }
    }
}



Tasarım Desenleri

Tasarım Desenlerine Giriş-1 (AbstractFactory)

Nesne yönelimli proglamlama yaparken bir takım desenleri bilmemiz bizim uygun modellemeyi yapabilmemizi hızlandıracaktır. Bu blogta bir takım problemlere çözümler getiren modelleri paylaşacağım. Uluslar arası kabul görmüş bu yapılarla nesne yönelimli programlama tekniğinizi geliştirebilirsiniz.

AbstractFactory
Bu tasarım deseninde bir sınıf oluşturulmadan önce aldığı parametre ile istenen işlemleri yapacak sınıfların oluşturulması ve bu sayede çalıştırılacak olan methodların yapılacak işe göre şekillenmesi sağlanmış olur. Örneğin müşterinin bizden istediği ürüne göre (constructure parametresi) bizim cihaz üretmemizi düşünelim. Öncelikle bu cihazı üretecek aletleri temin ederiz (nesneleri oluştururuz.) . Daha sonra işlemleri yaparız( methodları çağırırız). Örnek vererek konu daha net anlaşılacaktır. Bir fabrikada opel astra ve opel vectra yapıldığını düşünelim. Fakat bu fabrikada opel astranında farklı modelleri  yapılabilmekte o zaman fabrikayı özelleştirmemiz gerekiyor. Fabrikadan miras alıp nihayi ürünü çıkaracak ufak atölyeler yapmamız gerekiyor. Sonuçta bu atölyeden farklı model arabalar çıkacaktır. Opel astra 2001 veya 2005 gibi.

        class MainProgram
    {

        public static void Main()
        {
           
            OpelFabrikasi factory1 = new Model2001Atolyesi();
            Araba client1 = new Araba(factory1);
            client1.Run();
        
            OpelFabrikasi factory2 = new Model2005Atolyesi();
            Araba client2 = new Araba(factory2);
            client2.Run();

            Console.ReadKey();
        }
    }
    abstract class OpelFabrikasi
    {
        public abstract OpelAstra OpelAstraUret();
        public abstract OpelVectra OpelVectraUret();
    }
    class Model2001Atolyesi : OpelFabrikasi
    {
        public override OpelAstra OpelAstraUret()
        {
            return new OpelAstra2001();
        }
        public override OpelVectra OpelVectraUret()
        {
            return new OpelVectra2001();
        }
    }
    class Model2005Atolyesi : OpelFabrikasi
    {
        public override OpelAstra OpelAstraUret()
        {
            return new OpelAstra2005();
        }
        public override OpelVectra OpelVectraUret()
        {
            return new OpelVectra2005();
        }
    }
    abstract class OpelAstra
    {
    }

    abstract class OpelVectra
    {
        public abstract void Interact(OpelAstra a);
    }

    class OpelAstra2001 : OpelAstra
    {

    }
    class OpelVectra2001 : OpelVectra
    {
        public override void Interact(OpelAstra a)
        {
            Console.WriteLine(this.GetType().Name +
              "==> " + a.GetType().Name);
        }
    }

    class OpelAstra2005 : OpelAstra
    {
    }

    class OpelVectra2005 : OpelVectra
    {
        public override void Interact(OpelAstra a)
        {
            Console.WriteLine(this.GetType().Name +
              " ==> " + a.GetType().Name);
        }
    }

    class Araba
    {
        private OpelAstra opelAstra;
        private OpelVectra opelVectra;

        public Araba(OpelFabrikasi factory)
        {
            opelVectra = factory.OpelVectraUret();
            opelAstra = factory.OpelAstraUret();
        }

        public void Run()
        {
            opelVectra.Interact(opelAstra);
        }
    }