ASP.NET Core N-Tier Architecture (Dev Rehber)

Lothric

Ar-Ge Ekibi
11 Şub 2024
15
13
Hunter's Dream
N-Tier Architecture

Merhaba, bu konumda sizlere N-Tier Architecture’den (Çok katmanlı mimari) bahsedeceğim ve ASP.NET CORE’da çok katmanlı bir proje oluşturacağız. Projeyi oluştururken sizlere nelere dikkat edilmeli ve neler önemli onları anlatmaya çalışacağım.



İlk önce
N-tire Architecture neden kullanılır ve kullanmalıyız?

N-tire Architecture, yazılım geliştirme sürecini daha yapılandırılmış, yönetilebilir ve esnek hale getirir. Bu mimari model, uygulamanın modülerliğini, yeniden kullanılabilirliğini, ölçeklenebilirliğini ve güvenliğini artırarak, yazılımın bakımını ve geliştirilmesini kolaylaştırır. Bu nedenle, büyük ve karmaşık projelerde veya uzun vadeli sürdürülebilirlik gerektiren yazılımlarda N-tire Architecture kullanılması yaygın bir yaklaşımdır.


Neden kullanmamız gerektiğini anladıysak kısaca bu katmanlı mimarinin içeriği yapısı nasıl bir şeymiş birlikte göz atalım.

N katmanlı mimari, genellikle üç ana katman üzerinde yapılandırılır, ancak "N" ifadesi, katman sayısının ihtiyaca göre artırılabileceğini belirtir. Temel katmanlar şunlardır:




Sunum Katmanı (Presentation Layer):

Kullanıcı arayüzünü ve kullanıcı ile etkileşimi yöneten katmandır. Web tarayıcıları, mobil uygulamalar veya masaüstü uygulamaları bu katmanda bulunur. Görevi, kullanıcının girdiği verileri alıp iş mantığı katmanına göndermek ve sonuçları kullanıcıya sunmaktır.

İş Mantığı Katmanı (Business Logic Layer):

Uygulamanın iş kurallarını ve iş mantığını içerir. Verilerin nasıl işlendiği, hangi işlemlerin yapılacağı gibi işlevler bu katmanda yer alır. Sunum katmanından gelen verileri alır, gerekli işlemleri yapar ve sonuçları geri döndürür.

Veri Erişim Katmanı (Data Access Layer):

Veri tabanı veya diğer veri depolama sistemleriyle etkileşimi yöneten katmandır. Verilerin okunması, yazılması, güncellenmesi ve silinmesi işlemlerini gerçekleştirir. İş mantığı katmanına veri sağlar ve iş mantığı katmanından gelen veri değişikliklerini veri depolama sistemine uygular.



Varlık Katmanı (Entity Layer):

Yazılım uygulamalarında veri modellerini tanımlayan ve bu modellerin doğruluğunu ve bütünlüğünü sağlayan kritik bir katmandır. Bu katman, uygulamanın veri yapısını iş mantığından ayırarak daha modüler, esnek ve yönetilebilir bir mimari sağlar.



Evet projeye başlamadan önce bu yapı hakkında bilgi edindiğimize göre projemize başlayabiliriz.




PROJE

Projeye ön bakış

Oluşturacağımız projemiz bir portfolyo sitesi olacak. Sitemizde sitenin sahibine ait bilgilerin olduğu bir karşılama ekranı, altında site sahibine ait olan hakkında sayfası onun altında da site sahibinin yeteneklerinin listeleneceği bir sayfa olacak.



Projenin Oluşturulması

Visual Studio’muzu açıyoruz ve yeni bir proje oluşturuyoruz. Proje şablonlarını gördüğümüz ekranda seçeceğimiz şablon “ASP.NET Core Web Uygulaması (Model-Görünüm-Denetleyici)” (Eğer siz İngilizce kullanıyorsanız şablonun ismi “ASP.NET Core Web App (Model-View-Controller)” olacaktır.)

Şablonu seçip projemize ismini veriyoruz. Sonraki dedikten sonra .NET sürümümüzü 6.0 seçiyoruz. Projemizi oluşturduğumuzda bize boş bir sayfa veriyor. Bu sayfayı kapattıktan sonra çözüm gezgininden projemize göz atabiliriz. Gördüğünüz üzere hazır olarak bir sürü klasör geldi, bu klasörleri kullanacağımız zaman geldiğinde size kısaca açıklayacağım.



Katmanların Oluşturulması

Çözüm gezgininden en yukarıda olan gerçek projemize sağ tıklayıp Ekle dedikten sonra Yeni Proje’ye tıklıyoruz. ASP.NET Core projemizin içine katmanlarımızı eklemek için küçük projeler ekleyeceğiz gibi düşünebilirsiniz.





Arama yerine Sınıf Kitaplığı yazdıktan sonra bu proje şablonuna tıklayıp ilkinin adına “EntityLayer” diyoruz. Bu Sınıf Kitaplığını oluşturacağımız 3 katman içinde ekleyeceğiz. Diğer katmanların isimlerine “DataAccessLayer” ve “BusinessLayer” diyoruz.


Katmanları oluşturacağımız kısımda yapacağımız son 2 işlem kaldı. İlki bütün katmanlarda olması gereken NuGet paketlerini kurma. Projemize sağ tıklayıp Çözüm için NuGet Paketlerini yönete tıklıyoruz. Buradan bütün katmanlarımıza tek seferde paketlerimizi indirebiliriz. İndireceğimiz paketler:



Bu paketleri indirmeden önce aşağıdaki görseldeki gibi bütün katmanlarımızın seçili olduğuna ve hangi .NET sürümüyle çalışıyorsak onla aynı sürümde indirdiğimize dikkat etmemiz gerekiyor.



İkinci işlem ise proje başvurularını düzgün kurmak. Burada birden fazla katmanla çalıştığımız için

projeler yani katmanlar arasındaki başvuruların nasıl işleyeceğini bilmemiz gerekiyor. DataAccessLayer katmanı EntityLayer’ı içine alıcak. BusinessLayer hem EntityLayer’ı hem de DataAccessLayer’ı içine alıcak. Sunum katmanımızda bütün katmanları içine alıcak.

Başvuru ekleyeceğimiz katmana sağ tıklayıp Ekle dedikten sonra Proje Başvurusu…’na tıklıyoruz ve bu ekrandan yukarıda anlattığım yapıya göre başvurularımızı ekliyoruz. Katmanlarımızın temelini bu şekile bitiriyoruz.






Entity Layer

EntityLayer katmanımıza sağ tıklayıp klasör ekle diyoruz. Klasörün ismi “Concrete” olacak ve bu klasörün içine Databasede karşılık gelecek tablolarımızın sınıflarını oluşturacağız. Portfolyo sayfamızda önceden belirttiğim gibi 3 bölüm olacaktı. Bu 3 bölüm için “Feature”, “About” ve “Skill” isimli classlarımızı Concrete klasörü içine oluşturuyoruz ve aşağıdaki kodları yazıyoruz.

C#:
namespace EntityLayer.Concrete
{
    public class Feature
    {
        [Key]
        public int FeatureID { get; set; }
        public string Header { get; set; }
        public string Name { get; set; }
        public string Title { get; set; }
    }
}


C#:
namespace EntityLayer.Concrete
{
    public class Skill
    {
        [Key]
        public int SkillID { get; set; }
        public string Title { get; set; }
        public string Value { get; set; }
    }
}

C#:
namespace EntityLayer.Concrete
{
    public class About
    {
        [Key]
        public int AboutID { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string Age { get; set; }
        public string Mail { get; set; }
        public string Phone { get; set; }
        public string Address { get; set; }

    }
}
Sınıfları tablo, property’leri ise sütun olarak görebilirsiniz.



Data Access Layer

DataAccesLayer katmanımıza sağ tıklayıp ilk öncelikle Contexts isimli bir klasör ekliyoruz. Bu klasörün içine de ProjeContext isimli bir class ekliyoruz. Class’ımızın içine aşağıdaki kodları yazıyoruz.
C#:
namespace DataAccessLayer.Contexts
{
    public class ProjeContext:DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("server=Sqlİsminiz\\SQLEXPRESS;database=Databaseİsmi;integrated security=true");
        }
        public DbSet<Feature> Features { get; set; }
        public DbSet<About> Abouts { get; set; }
        public DbSet<Skill> Skills { get; set; }
    }
}

Context sınıfımız veri tabanı ile proje arasındaki bağı kuracak ve database’imizi oluşturacak.
Bu işlemleri yaptıktan sonra yukarıda bulunan Görünüm=>Diğer pencereler=>Paket yöneticisi konsoluna tıklıyoruz.

Paket yöneticisi konsoluna “add-migration mig_create_database” yazıyoruz. Konsola bu komutumuzu yazmadan önce konsolun üstünde seçili olan “Varsayılan proje:”nin
DataAccessLayer olduğuna dikkat edin. Eğer içinde context sınıfı yer almayan bir katmanı seçerseniz konsol hata verecektir. Bunu yazdığımızda önümüze oluşturduğumuz migration’umuz gelecek. Bu sınıfta birazdan oluşturacağımız veri tabanının bütün özelliklerine göz atıp değiştirebilirsiniz. Sonrasına konsola “update-database” dedikten sonra veri tabanımız oluşturuluyor.



Veri tabanımız oluştuktan sonra
DataAccessLayer katmanımıza sağ tıklayıp 2 tane daha klasör oluşturacağız. Birinci klasörün ismi Abstract diğerinin ismi
Concrete olucak. Kısaca anlatmak gerekitse Abstract klasörü tanımlayacağımız interface’leri, Concrete ise tanımlayacağımız sınıflarımızı tutacak.



Abstract klasörünün içerisinde her tablomuz için bir interface tanımlayacağız ve bu interface’lerin içerisinde tablolar için kullanacağımız metotların imzalarını atacağız. Şimdi bir düşünelim, bizim projemizde 50 tablo olsun. Bu 50 tablo içinde teker teker interface’lerin içlerine teker teker aynı metodları tekrar tekrar yazmak çok zor olur. Bu yüzden projeye esneklik sağlamak için burada bir generic yapı kuracağız. Bir tane Generic interface tanımlayacağız ve bu interface’in içinde bütün tablolarımızda ortak kullanacağımız metotları bir kere tanımlayacağız.



Generic interface’imizi oluşturalım.
Abstract klasörüne sağ tıklayıp “IGenericDal” isimli bir interface ekliyoruz. Oluşturduğumuz interface’in içine aşağıdaki kodları yazıyoruz.

C#:
namespace DataAccessLayer.Abstract
{
    public interface IGenericDal<T> where T : class, new()
    {
        void Insert(T t);
        void Update(T t);
        void Delete(T t);
        List<T> GetList();
        T GetByID(int id);
    }
}

IGenericDal’dan sonra her sınıfımız için interface’lerimizi oluşturuyoruz. Burada isimlendirmeye dikkat edin. İsimlendirme projenin çalışması için bir gereklilik olmasa da sizin projeyi oluştururken daha düzenli bir şekilde ilerlemenize yardımcı olur. Bir de dışarıdan bakan bir yazılımcının daha kolay anlamasını sağlamış olursunuz. “IFeatureDal”, “IAboutDal” ve “ISkillDal” isimli interface’lerimizi abstract klasörüne ekliyoruz.



Interface’lerimizin içi “public interface çalıştığımızinteface:IGenericDal<ÇalıştığımızTablo>” gibi olmalıdır.

C#:
namespace DataAccessLayer.Abstract
{
    public interface IAboutDal:IGenericDal<About>
    {
    }
}

C#:
namespace DataAccessLayer.Abstract
{
    public interface ISkillDal:IGenericDal<Skill>
    {
    }
}
C#:
namespace DataAccessLayer.Abstract
{
    public interface IFeatureDal:IGenericDal<Feature>
    {
    }
}
3 tablomuz içinde örnekteki gibi kodlarımızı yazıp Abstract klasörünü bitiriyoruz. Şimdi Concrete klasörüne sağ tıklayıp 2 tane daha klasör ekleyeceğiz. İlkinin ismi “Repository” diğerinin ismi “EntityFramework” olacak. Concrete klasöründede Abstract klasöründe kurduğumuz yapıya benzer bir generic yapı kuracağız.



Repository klasörüne sağ tıklayıp “GenericRepository” isimli sınıf ekleyeceğiz. Bu sınıf bizim abstract klasöründe imzasını attığımız metotların içini dolduracak. “public class GenericRepository” yazan satırın hemen sağına “<T> : IGenericDal<T> where T : class, new()” yazıyoruz. Bunu yazdığımızda fark ettiyseniz IGenericDal’ın altını kırmızıyla çizicek. Bunun sebebi arabirimi bu sınıfa uygulamamızdan kaynaklanıyor. Hatanın üstüne ctlr .(nokta) dedikten sonra arabirimi uygula diyoruz. Burada metotların içleri boş gelecek ama biz aşağıdaki kodumuzdaki gibi dolduracağız.

C#:
namespace DataAccessLayer.Concrete.Repository
{
    public class GenericRepository<T> : IGenericDal<T> where T : class, new()
    {
        public void Delete(T t)
        {

            using var c = new ProjeContext();
            c.Remove(t);
            c.SaveChanges();
        }

        public T GetByID(int id)
        {
            using var c = new ProjeContext();
            return c.Set<T>().Find(id);
        }

        public List<T> GetList()
        {
            using var c = new ProjeContext();
            return c.Set<T>().ToList();
        }

        public void Insert(T t)
        {
            using var c = new ProjeContext();
            c.Add(t);
            c.SaveChanges();
        }

        public void Update(T t)
        {
            using var c = new ProjeContext();
            c.Update(t);
            c.SaveChanges();
        }
    }
}


EntityFramework klasörüne her sınıfımız için class ekleyeceğiz. Bu sınıfların isimlendirilmesi “EfAboutDal” gibi olacak. Her class’ın içine aşağıdaki kodları yazıp Data Access Layer katmanımızda ki ikinci generic yapıyı kurmuş olacağız.
C#:
namespace DataAccessLayer.Concrete.EntityFramework
{
    public class EfAboutDal:GenericRepository<About>,IAboutDal
    {
    }
}

C#:
namespace DataAccessLayer.Concrete.EntityFramework
{
    public class EfFeatureDal:GenericRepository<Feature>,IFeatureDal
    {
    }
}
C#:
namespace DataAccessLayer.Concrete.EntityFramework
{
    public class EfSkillDal:GenericRepository<Skill>,ISkillDal
    {
    }
}

BusinessLayer

BusinessLayer katmanımıza da DataAccessLayer katmanımızdaki gibi “Abstract” ve “Concrete” isimli iki klasör tanımlıyoruz. Burada tekrardan işimizi kolaylaştırmak için bir generic yapı kuracağız. Abstract klasörüne sağ tıklayıp bir interface ekliyoruz. Interface’in ismi “IGenericService” olucak. Aşağıdaki kodları bu interface’e yazıyoruz.
C#:
namespace BusinessLayer.Abstract
{
    public interface IGenericService<T> where T : class, new()
    {
        void TAdd(T t);
        void TDelete(T t);
        void TUpdate(T t);
        List<T> TGetList();
        T TGetByID(int id);

    }
}


IGenericService’ten sonra DataAccessLayer’da da yaptığımıza benzer şekilde her tablomuz için bir interface oluşturacağız. Bunların isimlendirmeleri “IAboutService”, IFeatureService” ve “ISkillService” olucak. Aşağıdaki kodları bu interface’lere yazıyoruz.
C#:
namespace BusinessLayer.Abstract
{
    public interface IAboutService:IGenericService<About>
    {
    }
}
C#:
namespace BusinessLayer.Abstract
{
    public interface IFeatureService:IGenericService<Feature>
    {
    }
}


C#:
namespace BusinessLayer.Abstract
{
    public interface ISkillService:IGenericService<Skill>
    {
    }
}

Abstract klasörü bittikten sonra Concrete klasörüne geliyoruz ve burada her bir tablomuz için bir manager sınıfı oluşturuyoruz. İsimleri “FeatureManager”, “AboutManager” ve “SkillManager” şeklinde olucak.



Manager sınıfının isminin aldığı satıra : yazıp hangi manager sınıfıyla çalışıyorsak onun interface’ini yazıyoruz. Burada GenericRepository sınıfında olduğu gibi altını kırmızıyla çekicektir. Aynı şekilde ara birimi uygula diyoruz ve kodlarımızı yazıyoruz.

C#:
namespace BusinessLayer.Concrete
{
    public class SkillManager : ISkillService
    {
        ISkillDal _skillDal;

        public SkillManager(ISkillDal skillDal)
        {
            _skillDal = skillDal;
        }

        public void TAdd(Skill t)
        {
            _skillDal.Insert(t);
        }

        public void TDelete(Skill t)
        {
            _skillDal.Delete(t);
        }

        public Skill TGetByID(int id)
        {
            return _skillDal.GetByID(id);
        }

        public List<Skill> TGetList()
        {
            return _skillDal.GetList();
        }

        public void TUpdate(Skill t)
        {
            _skillDal.Update(t);
        }
    }
}

C#:
namespace BusinessLayer.Concrete
{
    public class FeatureManager : IFeatureService
    {
        IFeatureDal _featureDal;

        public FeatureManager(IFeatureDal featureDal)
        {
            _featureDal = featureDal;
        }

        public void TAdd(Feature t)
        {
            _featureDal.Insert(t);
        }

        public void TDelete(Feature t)
        {
            _featureDal.Delete(t);
        }

        public Feature TGetByID(int id)
        {
            return _featureDal.GetByID(id);
        }

        public List<Feature> TGetList()
        {
           return _featureDal.GetList();
        }

        public void TUpdate(Feature t)
        {
            _featureDal.Update(t);
        }
    }
}

C#:
namespace BusinessLayer.Concrete
{
    public class AboutManager : IAboutService
    {
        IAboutDal _aboutDal;

        public AboutManager(IAboutDal aboutDal)
        {
            _aboutDal = aboutDal;
        }

        public void TAdd(About t)
        {
            _aboutDal.Insert(t);
        }

        public void TDelete(About t)
        {
            _aboutDal.Delete(t);
        }

        public About TGetByID(int id)
        {
            return _aboutDal.GetByID(id);
        }

        public List<About> TGetList()
        {
            return _aboutDal.GetList();
        }

        public void TUpdate(About t)
        {
            _aboutDal.Update(t);
        }
    }
}


Presentation Layer

Bu katmanda ilk yapacağımız işlem temamızı projeye dahil etmek. Presentation Layer katmanımızın içerisindeki “wwwroot” klasörüne temamızın dosyalarını ekliyoruz. Bu dosyaları konunun sonunda bırakacağım github linkinden bulabilirsiniz. (Github’ta projenin içerisine baktığınızda wwwroot’un içinde Template isimli bir klasör olması lazım.)



Temayı projeye dahil ettikten sonra
Controllers klasörüne sağ tıklayıp yeni bir denetleyici (controller) ekle diyoruz. MVC Denetleyicisi – Boş şeklinde olacak. Kısaca bahsetmek gerekirse denetleyiciler bizim temel crud işlemlerini ve metotlarımızı çağıracağımız kod sayfaları olacak.
Denetleyicimiz oluştuğunda ilk başta bir IActionResult metotuyla geliyor, ve bu metot sadece View döndürüyor. Index metotunun üstüne sağ tıklayıp Görünüm ekle diyoruz. Sonrasında Razor Görünümü diyoruz. Bu ekranda birşeye dokunmadan Ekle diyoruz.



Eklediğimiz View'ımızı konunun sonunda doldurmuş olucaz. Bahsetmek gerekirse View’lar bizim fronted tarafımızı oluşturacak kod sayfaları olacak. View’lar Html ve c# kod yapısının birleşik kullanıldığı sayfalardır.

Yalnız biz bütün işlemlerimizi tek bir View sayfası üzerinden gerçekleştiremeyiz. Bu yüzden View Component yapısını kullanacağız. Bu yapıyı kullanmak için ilk öncelikle
Presentation Layer katmanımıza sağ tıklayıp “ViewComponents” isimli bir klasör oluşturuyoruz. Klasörümüzün içine “Default” isimli bir klasör daha ve bu klasörün içine de “FeatureList” isimli bir class oluşturuyoruz. Class’ımızın içine aşağıdaki kodları yazıyoruz.
C#:
namespace Nkatmanli.ViewComponents.Default
{
    public class FeatureList : ViewComponent
    {
        FeatureManager featureManager = new FeatureManager(new EfFeatureDal());

        public IViewComponentResult Invoke()
        {
            var values = featureManager.TGetList();
            return View(values);
        }
    }
}

Yukarıdaki kodda Business Layer katmanımızda oluşturduğumuz FeatureManager sınıfı çağırıyoruz. Bu sayede FeatureManager sınıfındaki metotlara erişimimiz oluyoruz. Feature tablosunu listelemek için Invoke metotunun içine koddaki gibi bir yapı kuruyoruz. View Component yapısını kurmaya devam etmek için View klasörünün içindeki Shared klasörüne “Components” isimli bir klasör oluşturuyoruz. Bu klasörün içine ise ViewComponents klasöründe oluşturduğumuz sınıfla aynı isimli bir klasör oluşturuyoruz. Bu klasörün içinede “Default” isimli bir view ekliyoruz. Ancak viewimiz bir partial view olucağından Kısmi görünüm olarak oluştur seçeneğine tik atıyoruz. View’ımız oluştuktan sonra aşağıdaki kodları yazıyoruz


HTML:
@using EntityLayer.Concrete
@model List<Feature>

<header>
    <div class="cover bg-light">
        <div class="container px-3">
            <div class="row">
                <div class="col-lg-6 p-2"><img class="img-fluid" src="~/Template/images/illustrations/hello3.svg" alt="hello" /></div>
                <div class="col-lg-6">
                    @foreach (var item in Model)
                    {
                        <div class="mt-5">
                            <p class="lead text-uppercase mb-1">@item.Header</p>
                            <h1 class="intro-title marker" data-aos="fade-left" data-aos-delay="50">@item.Name</h1>
                            <p class="lead fw-normal mt-3" data-aos="fade-up" data-aos-delay="100">@item.Title</p>

                            <div class="mt-3" data-aos="fade-up" data-aos-delay="200"><a class="btn btn-primary shadow-sm mt-1 hover-effect" href="#about">Göz At <i class="fas fa-arrow-right"></i></a></div>
                        </div>
                    }
                </div>
            </div>
        </div>
    </div>
    <div class="wave-bg"></div>
</header>



Yapımız kısaca bu görseldeki gibi gözükecektir. Feature için view componentimizi oluşturduk geriye diğer 2 tablomuz içinde oluşturmak kaldı
AboutList Kodları:
C#:
namespace Nkatmanli.ViewComponents.Default
{
    public class AboutList : ViewComponent
    {
        AboutManager aboutManager = new AboutManager(new EfAboutDal());
        public IViewComponentResult Invoke()
        {
            var values = aboutManager.TGetList();
            return View(values);
        }
    }
}

SkillList Kodları:
C#:
namespace Nkatmanli.ViewComponents.Default
{
    public class SkillList : ViewComponent
    {
        SkillManager skillManager = new SkillManager(new EfSkillDal());
        public IViewComponentResult Invoke()
        {
            var values = skillManager.TGetList();
            return View(values);
        }
    }
}

AboutList View Kodları:
HTML:
@using EntityLayer.Concrete
@model List<About>
<div class="section pt-4 px-3 px-lg-4" id="about">
    <div class="container-narrow">
        @foreach (var item in Model)
        {


            <div class="row">
                <div class="col-md-6">
                    <h2 class="h4 my-2">@item.Title</h2>
                    <p>@item.Description</p>
                    <div class="row mt-3">
                        <div class="col-sm-2">
                            <div class="pb-1">Yaş: </div>
                        </div>
                        <div class="col-sm-10">
                            <div class="pb-1 fw-bolder">@item.Age</div>
                        </div>
                        <div class="col-sm-2">
                            <div class="pb-1">Mail:</div>
                        </div>
                        <div class="col-sm-10">
                            <div class="pb-1 fw-bolder">@item.Mail</div>
                        </div>

                        <div class="col-sm-2">
                            <div class="pb-1">Telefon:</div>
                        </div>
                        <div class="col-sm-10">
                            <div class="pb-1 fw-bolder">@item.Phone</div>
                        </div>
                        <div class="col-sm-2">
                            <div class="pb-1">Adres:</div>
                        </div>
                        <div class="col-sm-10">
                            <div class="pb-1 fw-bolder">@item.Address</div>
                        </div>

                    </div>
                </div>
                @*<div class="col-md-5 offset-md-1" data-aos="fade-left" data-aos-delay="100"><img class="avatar img-fluid mt-2" src="@item.ImageUrl" width="400" height="400" alt="Walter Patterson" /></div>*@
            </div>
        }
    </div>
</div>

SkillList View Kodları:
C#:
@using EntityLayer.Concrete
@model List<Skill>


<div class="section px-3 px-lg-4 pt-5" id="skills">
    <div class="container-narrow">
        <div class="text-center mb-5">
            <h2 class="marker marker-center">Yeteneklerim</h2>
        </div>
        <div class="text-center">
            <p class="mx-auto mb-3" style="max-width:600px">Aşağıda bildiğim proglamlama dilleri ve teknolojiler üzerine bir bar listesi bulunmaktadır. Yeni bir programlama dilini öğrenmek benim için oldukça hızlı gerçekleşen bir durumdurs</p>
        </div>
        <div class="bg-light p-3">
            <div class="row">
                <div class="col-md-12">
                    @foreach (var item in Model)
                    {


                        <div class="py-1">
                            <div class="d-flex text-small fw-bolder"><span class="me-auto">@item.Title</span><span>@item.Value</span></div>
                            <div class="progress my-1">
                                <div class="progress-bar bg-primary" role="progressbar" data-aos="zoom-in-right" data-aos-delay="100" data-aos-anchor=".skills-section" style="width: @item.Value" aria-valuenow="@item.Value" aria-valuemin="0" aria-valuemax="100"></div>
                            </div>
                        </div>
                    }
                </div>

            </div>
        </div>
    </div>
</div>
Bütün Componentlerimizi oluşturduktan sonra Default controllerımızın içindeki Index view’indeki kodlar şu şekilde olmalıdır:

HTML:
@{
    Layout = null;
}

<!DOCTYPE html>
<!-- upto 2 directory depth-->
<html lang="en-US">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Super Folio</title>
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="crossorigin" />
    <link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;700;800&amp;display=swap" />
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;700;800&amp;display=swap" media="print" onload="this.media='all'" />
    <noscript>
        <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;700;800&amp;display=swap" />
    </noscript>
    <link href="~/Template/css/font-awesome/css/all.min.css?ver=1.2.0" rel="stylesheet">
    <link href="~/Template/css/bootstrap-icons/bootstrap-icons.css?ver=1.2.0" rel="stylesheet">
    <link href="~/Template/css/bootstrap.min.css?ver=1.2.0" rel="stylesheet">
    <link href="~/Template/css/aos.css?ver=1.2.0" rel="stylesheet">
    <link href="~/Template/css/main.css?ver=1.2.0" rel="stylesheet">
    <noscript>
        <style type="text/css">
            [data-aos] {
                opacity: 1 !important;
                transform: translate(0) scale(1) !important;
            }
        </style>
    </noscript>
</head>
<body id="top">
    <header class="bg-light">
        <nav class="navbar navbar-expand-lg navbar-light bg-light" id="header-nav" role="navigation">
            <div class="container">
                <a class="link-dark navbar-brand site-title mb-0" href="#">Super Folio</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <ul class="navbar-nav ms-auto me-2">
                        <li class="nav-item"><a class="nav-link" href="#about">About</a></li>
                        <li class="nav-item"><a class="nav-link" href="#skills">Skills</a></li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="page-content">
        <div id="content">
            @await Component.InvokeAsync("FeatureList")
            @await Component.InvokeAsync("AboutList")
            @await Component.InvokeAsync("SkillList")
            <br />
            <br />
            <br />
            <footer class="pt-4 pb-4 text-center bg-light">
                <div class="container">
                    <div class="my-3">
                        <div class="h4">Walter Patterson</div>
                        <p>Web Developer & Mobile Application Developer</p>
                        <div class="social-nav">
                            <nav role="navigation">
                                <ul class="nav justify-content-center">
                                    <li class="nav-item"><a class="nav-link" href="https://twitter.com/templateflip" title="Twitter"><i class="fab fa-twitter"></i><span class="menu-title sr-only">Twitter</span></a></li>
                                    <li class="nav-item"><a class="nav-link" href="https://www.facebook.com/templateflip" title="Facebook"><i class="fab fa-facebook"></i><span class="menu-title sr-only">Facebook</span></a></li>
                                    <li class="nav-item"><a class="nav-link" href="https://www.instagram.com/templateflip" title="Instagram"><i class="fab fa-instagram"></i><span class="menu-title sr-only">Instagram</span></a></li>
                                    <li class="nav-item"><a class="nav-link" href="https://www.linkedin.com/" title="LinkedIn"><i class="fab fa-linkedin"></i><span class="menu-title sr-only">LinkedIn</span></a></li>
                                    <li class="nav-item"><a class="nav-link" href="https://www.behance.net/templateflip" title="Behance"><i class="fab fa-behance"></i><span class="menu-title sr-only">Behance</span></a></li>
                                </ul>
                            </nav>
                        </div>
                    </div>
                    <div class="text-small text-secondary">
                        <div class="mb-1">&copy; Super Folio. All rights reserved.</div>
                        <div>
                            <!-- Make sure to buy a license for the template before removing the line below. Buy license on https://templateflip.com/ -->Design - <a href="https://templateflip.com/" target="_blank">TemplateFlip</a>
                        </div>
                    </div>
                </div>
            </footer>
        </div>
    </div>
    <div id="scrolltop"><a class="btn btn-secondary" href="#top"><span class="icon"><i class="fas fa-angle-up fa-x"></i></span></a></div>
    <script src="~/Template/scripts/imagesloaded.pkgd.min.js?ver=1.2.0"></script>
    <script src="~/Template/scripts/masonry.pkgd.min.js?ver=1.2.0"></script>
    <script src="~/Template/scripts/BigPicture.min.js?ver=1.2.0"></script>
    <script src="~/Template/scripts/purecounter.min.js?ver=1.2.0"></script>
    <script src="~/Template/scripts/bootstrap.bundle.min.js?ver=1.2.0"></script>
    <script src="~/Template/scripts/aos.min.js?ver=1.2.0"></script>
    <script src="~/Template/scripts/main.js?ver=1.2.0"></script>
</body>
</html>



Projemizi çalıştırırsak artık sayfada eskiden yazan yazıların artık olmadığını görebiliriz. Bunun sebebi buradaki yazıları artık oluşturduğumuz tablolara bağladık. Dolayısıyla tablolarımız boş olduğu için yazılarımız boş geliyor. Veri tabanından elle veri girersek buraya verilerimiz gelecektir.



Bu sayede projemizi kısmende olsa bitirmiş olduk. Projenin dosyalarına ve Admin panelli haline buradaki linkten: GitHub - lothriccc/Nkatmanli

Projenin büyük çapı haline bu linkten ulaşabilirsiniz: GitHub - lothriccc/NKatmanli-Genis-Capli
 
Üst

Turkhackteam.org internet sitesi 5651 sayılı kanun’un 2. maddesinin 1. fıkrasının m) bendi ile aynı kanunun 5. maddesi kapsamında "Yer Sağlayıcı" konumundadır. İçerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır. Turkhackteam.org; Yer sağlayıcı olarak, kullanıcılar tarafından oluşturulan içeriği ya da hukuka aykırı paylaşımı kontrol etmekle ya da araştırmakla yükümlü değildir. Türkhackteam saldırı timleri Türk sitelerine hiçbir zararlı faaliyette bulunmaz. Türkhackteam üyelerinin yaptığı bireysel hack faaliyetlerinden Türkhackteam sorumlu değildir. Sitelerinize Türkhackteam ismi kullanılarak hack faaliyetinde bulunulursa, site-sunucu erişim loglarından bu faaliyeti gerçekleştiren ip adresini tespit edip diğer kanıtlarla birlikte savcılığa suç duyurusunda bulununuz.