Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

System.ArgumentException: ‘Cannot Call Action Method ‘System.Collections.Generic.List`1[A] X[A](System.Func`2[A,System.Boolean])’ On Controller ‘XXX’ Because The Action Method Is a Generic Method.’ Hatası ve Çözümü

Merhaba,

Asp.NET MVC mimarisinde projenizi inşa ederken, oluşturulan adreslerin şablonlarını RouteConfig.cs dosyası yerine Route Attribute özelliğini kullanarak yapmayı tercih ettiğiniz durumda sizi karşılayacak olan olası bir hata üzerine konuşuyor olacağız. Bu olası hata, “olası” kelimesini vurguladığım gibi her durumda ceyran etmeyen, yapısal olarak özel bir inşa ve teknik kullanıldığı durumda karşılaşılabilen hususi bir durumu teşkil etmektedir. Öncelikle hatamızın ne olduğunu ele alalım, ardından nasıl bir durumda alındığına değindikten sonra karşılaşılan bu olası hataya nasıl çözüm getirebileceğimizi konuşalım.

Hatamız;

‘Cannot call action method ‘System.Collections.Generic.List`1[A] SartliVeriGetir[A](System.Func`2[A,System.Boolean])’ on controller ‘WebApplication38.Controllers.HomeController’ because the action method is a generic method.’

şeklindedir.
Hatamızın görsel halinide görmek icap ederse eğer;
System.ArgumentException: 'Cannot Call Action Method 'System.Collections.Generic.List`1[A] X[A](System.Func`2[A,System.Boolean])' On Controller 'XXX' Because The Action Method Is a Generic Method.' Hatası ve Çözümü
buyrun bu şekildedir.

Bu Hata Hangi Durumda Alınmaktadır?

Asp.NET MVC mimarisinde, controller sınıflarının türetileceği ve çoğu veritabanı modifikasyon işlemleriyle beraber tüm yapılanmada ortak olacak metot, property vs. ile birlikte diğer çalışmaları barındıran “BaseClass(Controller).cs” olarak nitelendirebileceğimiz sınıfı, kendisini ve içerisindeki yapıları Generic bir şekilde inşa ederek projenin senaryosuna dahil ediyorsanız ve tüm bunların yanında route mekanizmasında Route Attribute özelliği ile çalışma gerçekleştiriyorsanız bu hatayla karşılaşma ihtimaliniz olacaktır. Olacaktır diyorum çünkü aşağıda da bu bahsettiğim yaklaşımla beraber hata pozisyonunu ele aldığımız örnekte gözden kaçırdığımız bir noktadan dolayı bu hatanın meydana geldiğini göreceksiniz.

Örnek olarak aşağıdaki “BaseClass(Controller).cs” sınıfını ele alalım.

    public class BaseClassController<T> : Controller where T : class, new()
    {
        private NorthwindEntities db = new NorthwindEntities();
        public NorthwindEntities VeriTabani
        {
            get
            {
                return db;
            }
        }
        public List<T> SartliVeriGetir(Func<T, bool> Metod)
        {
            return db.Set<T>().Where(Metod).ToList();
        }
        public List<A> SartliVeriGetir<A>(Func<A, bool> Metod) where A : class, new()
        {
            return db.Set<A>().Where(Metod).ToList();
        }
        public List<T> TumVerileriGetir()
        {
            return db.Set<T>().ToList();
        }
        public List<A> TumVerileriGetir<A>() where A : class, new()
        {
            return db.Set<A>().ToList();
        }
    }

İşte bu “BaseClass”ı kullanarak aşağıdaki “Home(Controller).cs” misali tüm controller sınıflarımızı türettiğimizi düşünelim.

    public class HomeController : BaseClassController<FormCollection>
    {
        public ActionResult Index()
        {
            //.
            //. Diğer işlemler
            //.
            return View();
        }
    }

Bu işlemlerden sonra yukarılarda da bahsettiğimiz gibi Route Attribute özelliği için “App_Start” klasöründeki RouteConfig sınıfında bulunan RegisterRoutes metoduna da aşağıdaki gibi “MapMvcAttributeRoutes” metodunu ekleyelim.

    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapMvcAttributeRoutes();
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }

Burada hata durumunu örneklendirdiğimizden dolayı bir web projesiyle ilgili diğer tüm çalışmaların yapıldığını varsayarak projemizi derleyip çalıştıralım ve olası hatamız tarafından aşağıdaki gibi karşılanalım…
System.ArgumentException: 'Cannot Call Action Method 'System.Collections.Generic.List`1[A] X[A](System.Func`2[A,System.Boolean])' On Controller 'XXX' Because The Action Method Is a Generic Method.' Hatası ve Çözümü
İşte… Gördüğünüz gibi bu tarz bir “BaseClass” yaklaşımında ilgili hata tarafından uyarılmaktayız. Hadi gelin şimdi bu uyarılmanın neden kaynaklandığını tanımlayalım ve nasıl çözüleceğini ele alalım.

Hatanın Çözümü?

Yukarıdaki satırlarda hatamızın hangi durumda meydana geldiğini inceledik. Şimdi, öncelik olarak hatamızın neden kaynaklandığını tanımlayalım, ardından çözümü ele alalım.

Controller sınıflarımızın miras alacağı “BaseClass(Controller).cs” sınıfı yukarıdaki örnekte olduğu gibi generic bir yapıda inşa edilebilir. Bunda bir sıkıntı yoktur yok olmasına lakin sınıf elemanlarının modifierlarından(niteleyici) birisi oldukça önemlidir. Bu modifier metotların erişim belirleyicileridir.

“BaseClass” sınıfı generic bir yapıdayken Route Attribute özelliği kullanılmaya çalışılıyorsa eğer, “BaseClass” sınıfı içerisindeki metotların erişim belirleyicileri oldukça önem arzetmektedir. Eğer ki bu erişim belirleyicileri, “public” olarak seçilirse compiler bu metotları bir Action Metot olarak algılayacaktır. Eee haliyle Asp.NET MVC mimarisinde generic metotlardan action metot olamayacağından dolayı ilgili hatamız meydana gelecektir.

Haliyle hatamızın çözümü, “BaseClass” içerisindeki tüm metotların erişim belirleyicilerini doğru bir şekilde seçmekten geçmektedir. Bu tarz bir durumda en doğru erişim belirleyicisi “protected” olabilir.

    public class BaseClassController<T> : Controller where T : class, new()
    {
        private NorthwindEntities db = new NorthwindEntities();
        public NorthwindEntities VeriTabani
        {
            get
            {
                return db;
            }
        }
        protected List<T> SartliVeriGetir(Func<T, bool> Metod)
        {
            return db.Set<T>().Where(Metod).ToList();
        }
        protected List<A> SartliVeriGetir<A>(Func<A, bool> Metod) where A : class, new()
        {
            return db.Set<A>().Where(Metod).ToList();
        }
        protected List<T> TumVerileriGetir()
        {
            return db.Set<T>().ToList();
        }
        protected List<A> TumVerileriGetir<A>() where A : class, new()
        {
            return db.Set<A>().ToList();
        }
    }

Evet. Örneğimiz üzerinden hatanın çözümünü misallendirirsek eğer “BaseClass” üzerinde yukarıdaki gibi bir değişiklik yapmamız yeterli olacaktır. Bu şekilde projeyi derleyip çalıştırdığımız zaman ilgili hatanın ortadan kalktığını göreceksiniz.

Ek olarak şunuda belirtmek istiyorum ki, aslında bu hatayla karşılaşmak için illaki generic memberlar barındıran “BaseClass” gibi bir yapılanmaya gerek yoktur. Herhangi bir controller içerisinde “public” erişim belirleyicisine sahip generic bir metot tanımlanmışken, Route Attribute özelliğinin kullanılmaya çalışılması gene aynı hatayı verecektir.

    public class HomeController : BaseClassController<FormCollection>
    {
        public ActionResult Index()
        {
            return View();
        }
        public T ABC<T>() where T : class, new()
        {
            return new T();
        }
    }

misali…

Tabi Asp.NET MVC mimarisini profesyonel anlamda kullanan hiçbir yazılımcı bu tarz bir tanımlamayı herhangi bir controllerda yapmayacağı için ve şahsen katiyen tavsiyede etmediğim için, hatanın karşılaşılabilecek en olabilitesi yüksek durum olan “BaseClass” senaryosu üzerinden sizlere bir anlatımda bulunmayı tercih ettiğimi bilmenizi isterim.

Faydalanmanız dileğiyle…

Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar dilerim…

Bunlar da hoşunuza gidebilir...

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir