Open
Developer Aşamaları


Visual Studio'yu açalım ve App adında bir solution (Çözüm oluşturalım) (Projeye uygun dilediğiniz ismi verebiliriz)


Projemiz Soğan Mimarisi Prensibine uyum sağlayacaktır.
Solid prensibin, Bağımlılıkların Azaltılması prensibine uygun çalışacağız.Abstract ve Concrete metodlarımız olacak.
Projede bilgilere, Interfaceler'e sorgu vererek erişeceğiz.Interfaceler, üzerinden nesneler çağıracağız.
Bunda dolayı Ef, Dapper,Nhybernate gibi kullacağımız ORM aracının, pek önemli rolü olmayacaktır.

Projemize 5 katman oluşturalım.Bunlar;

 
    1.Core Katmanı  - Çekirdek Katmandır.Tüm projelerde, kullanılacak ortak katmandır.
     Projenin en temel verilerini barıdıracak, her katmandan erişim sağlayacak.
     Generic Repository(depo) sınıfını bu katmanda tanımlayacağız.
     Projede, ilerde farklı bir Orm aracı kullanırsak, Core katmanının IRepository sini kullanacağız.(Dapper, Nhybernate vb.)
     IData adında bir arabirim sınıfımız olacak, oluşturduğumuz modeller eğer bu interfaceden kalıtım alıyorsa bu veritabanı modeli olduğu anlamına  

   2.Dal Katmanı -  Dal Katmanı herzaman yazılımcıya özel bir katman olacaktır.
    DbContext ve Temel Repository metotlarımız bu katmanda olacaktır.

    3.Bll Katmanı -  Ek Repository lerimiz burda tanımlanacak.
    Unity of Work prensibimiz bu katmanda yer alacaktır.
    
    4.Model Katmanı -  Modellerimiz (Entity), gerekli ViewModellerimiz bu katmanda olacaktır.

    5.Ui(Web) Katmanı -  Kullanıcının arayüzüdür.Kullanıcı bu katmanda işlemlerini görecek. Web katmanı diye de isimlendirebiliriz.
    Core daki IData için Model katmanını referans vermemiş oluruz. Döngü engellenir.



Bu katmanda temel bir modelimiz ve temel bir de Repository'miz olacak.
Temel Modelimiz, bildirim sağlayan bir sınıf olacaktır. E_Bildiri,E_Result,ResultMessage gibi isimler verebiliriz.
IData adında bir arabirimimiz(interface) olacak.Maksat,Bu interfaceden kalıtım alan sınıflarn, veritabanı için kullanıldığını bildirmek.

Temel bir Repositorymiz olacak.IRepository ve BaseRepository.
BaseRepoitory, Generic yapıda olacak isteilen tabloya kalıtım verecektir.
Core Katmanına 3 klasör oluşturalım. Abstract,Concrete ve Model isimlerini verelim.

Model klasörüne E_Bildiri adında bir sınıf oluşturalım

    public class E_Bildiri<T>
    {
    public string Bildiri { get; set; }   //Bilgilendirmek için  (hata ya da mesaj)
    public bool Status { get; set; } //Geriye data dönsün istersek, bu property'nin içerisini doldurcaz.
    public T Data { get; set; } //Datanın Başarı Durumu kontrolu için
    }

Abstract klasörüne IData adında bir interface oluşturalım.

    public interface IData
    {
    }

Abstract klasörüne, IRepository adında bir interface oluşturalım.Generic türünde olacak.
Metodlarımızın imzalarını gösterecek.(Gövdelerini concrete'te tanımlıycaz)
Kural olarak oluşturduğumuz değişkenler, farklı ORM araclarıyla da, crud işlemlerini gerçekleştirilebilecek.
Where T : bir classdır, IData dan kalıtım alır, new lenebilir.

    public interface IRepository<T> where T:class,IData, new()
    {
    E_Bildiri<T> Add(T prm);
    E_Bildiri<T> Update(T prm);
    E_Bildiri<T> Delete(int ID);
    E_Bildiri<T> SoftDelete(int ID);
    E_Bildiri<ICollection<T>> GetAll(Expression<Func<T, bool>> filter = null);
    E_Bildiri<T> Get(Expression<Func<T, bool>> filter); //(Find)
    }

Concrete klasörüne, BaseRepository adında bir sınıf oluşturalım.Generic türünde olacak.
IRepository de kalıtım alacak (metotların imzalarını alıp metotların gövdesini oluşturacağız)
DbContext ten yararlanacağımız için bir de <TContext> ekleyelim.
Where T : bir classdır, IData dan kalıtım alır, new lenebilir.
where TContext : DbContext (TContext ise DbContextten türemiş bir nesnesidir.

    public class EFRepository<T, TContext> : IRepository<T> where T : class, IData, new() where TContext : class, new()
    {
      //TContext in üzerine gelip DbContext için oto. EF Core paketini projemize ekleyelim.
      //IRepository<T> yi implemente edelim (Ctrl+)Metotların imzaları gelecektir, gövdelerini dolduralım.
    }

BaseRepository'mizde metod eklerken, DbContext nesnesi oluşturmaya ihtiyacımız var.Bu da bir bağımlılık demek.
Şimdi en sağlıklı yöntemi, birlikte seçelim.

    //Her metodda DbContext nesnesi oluşturabiliriz.
        TContext ctx = new TContext();
        ctx.Add(prm);
        ctx.SaveChanges();
     //Ama  her metod için TContext ten yeni nesneler alırsak ramde kalır ve projemiz şişer.
    //işimiz bittiğinde dispose etsek (using kullansak).
    //nesne oluşacak,içindeki komutlar çalışacak,scope kapandığında nesne kendsini dispose edecek.
        using (var ctx= new TContext())  
        {
           ctx.Add(prm);  ...
        } 
    //Ram işini hallettik ama yine de bağımlılığı asıl bir sınıftan alan repository ler olacak. Bu da ilerde diğer ORM araçlarına sorun yaratır.
    //O zaman, Dependency Injection prensibine uyar ve Constructor ile bağımlılık alırız. 
    //TContext türünde ctx nesnemizin içi boştur. EfRepository çağrıldığında (doğum anında=constructor), bir parametre vererek içini bu nesne ile doldursun.
        readonly TContext ctx; 
        public EFRepository(TContext _prm) 
        {
            ctx = _prm;
        }  
    public E_Bildiri <T> Add(T prm)
    {
        try
        {
        var data = ctx.Entry(prm);
        data.State = EntityState.Added;
        //if (ctx.SaveChanges() > 0) //SaveChanges metodu integer değer döner.(Int deger null değil, 0 ile ilişkilendirilir.)
        //{
        //    return new E_Bildiri<T> { Data = prm, Bildiri = "başarılı", Status = true }; Dönrse hem datayı göster hem başarılı de hem status.
        //}
        //else
        //{
        //    return new E_Bildiri<T> { Bildiri = "hata", Status = false };
        //}
 
        //TernaryIF ile daha kısa kod yazarız.
        //return ctx.SaveChanges() > 0 ? new E_Bildiri { Data = prm, Bildiri = "başarılı", Status = true } : new E_Bildiri { Bildiri = "hata", Status = false };
               
        }
        catch (Exception ex)
        {
           return  new E_Bildiri<T> { Bildiri = ex.Message, Status = false, Data=prm };
        }
    }
        

    public E_Bildiri<T> AddAll(List<T> prm)
    {
    using (var ctx = new TContext())
    {
    ctx.AddRange(prm); //tek data ile olan metod işimize yaramaz. void olarak geri döner zaten değişkenede atayız
    return ctx.SaveChanges() > 0 ? new E_Bildiri<T> { Bildiri = "başarılı", Status = true } : new E_Bildiri<T> { Bildiri = "hata", Status = false };
    }
    }
    AddRange = nesneyi çekerken, listeyi tek seferde veririz ve nesnenin boyutundaki tüm üyeleri için o teker teker aynı işlemi yapar.


    public E_Bildiri<T> Delete(int ID)
    {
    try
    {
    using (TContext context = new TContext())
    {
    var data = context.Set<T>().Find(ID);
    var deletedData = context.Entry(data);  //Entry ile buluyor
    deletedData.State = EntityState.Deleted;
    int result = context.SaveChanges();
    //ternary if
    return result == 0 ? new E_Bildiri<T> { Status = false, Data = null, Bildiri = "Kayıt silinemedi." } : new E_Bildiri<T> { Status = true, Data = data, Bildiri = "Kayıt silindi." };
    }
    }
    catch (Exception ex)
    {
    return new E_Bildiri<T> { Status = false, Data = null, Bildiri = ex.Message };
    }
    }


    Eğer Delete metodunu int prm değil Entity ile alsaydık;
    //public E_Bildiri<T> Delete(T prm)
    //{
    //using (var ctx = new TContext())
    //{
    //    var data = ctx.Entry(prm);
    //    data.State = EntityState.Deleted;

    //    int sonuc = ctx.SaveChanges();
    //    return sonuc>0 ? new E_Bildiri<T>
    { Bildiri = "silindi", Data = prm, Status = true }:Bildiri = "hata", Data = prm, Status = false };
    //}




    public E_Bildiri<T>
    Get(Expression<Func<T, bool>> filter)
    {
    using (var ctx = new TContext())
    {
    var sonuc =ctx.Find<T>(filter); //Find<>(expresion bekler bizde filter ekledik)bilgiyi yakaladık.
    return new E_Bildiri<T> { Bildiri = "Getirildi", Data = sonuc, Status = true };
    }
    }

    public E_Bildiri<ICollection<T>> GetAll(Expression<Func<T, bool>> filter = null)
    {
    using (var ctx = new TContext())
    {
    List<T> sonuc;
    if (filter == null)
    {
    sonuc = ctx.Set<T>().ToList();  // !using System Linq
    }
    else
    {
    sonuc = ctx.Set<T>().Where(filter).ToList();  // !using System Linq
    }
    return new E_Bildiri<ICollection<T>> { Data = sonuc, Status = true, Bildiri = "data listelendi" };
    }
    }

    public E_Bildiri<T> SoftDelete(int ID)
    {
    try
    {
    using (var ctx = new TContext())
    {
    var data = ctx.Set<T>().Find(ID);
    var deletedata = ctx.Entry(data);

    deletedata.State = EntityState.Modified;
    return ctx.SaveChanges() > 0 ? new E_Bildiri<T> { Bildiri = "güncellendi", Data = data, Status = true } : new E_Bildiri<T> { Bildiri = "hata", Data = null, Status = false };
    }
    }
    catch (Exception ex)
    {

    return new E_Bildiri<T> { Bildiri = ex.Message, Data = null, Status = false };
    }
    }

    public E_Bildiri<T> Update(T prm)
    {
    try
    {
    using (var ctx = new TContext())
    {
    var data = ctx.Entry(prm);
    data.State = EntityState.Modified;

    return ctx.SaveChanges() > 0 ? new E_Bildiri<T> { Bildiri = "güncellendi", Data = prm, Status = true } : new E_Bildiri<T> { Bildiri = "hata", Data = null, Status = false };
    }
    }
    catch (Exception)
    {

    return new E_Bildiri<T> { Bildiri = ex.Message, Data = null, Status = false };
    }
    }

Oluşturduğumuz Model katmanına, ilk önce Base adında bir sınıf ekleyeceğiz.Temel propertyleri olacak.
Tüm ya da dilediğimiz modeller, bu modelden kalıtım alabilecek.

    public  class Base
    {
    public int ID { get; set; }
    public bool IsActive { get; set; }
    public bool IsDeleted { get; set; }
    public DateTime InsertDate { get; set; }
    public DateTime? UpdateDate { get; set; }
    //public Nullable<DateTime>   MyProperty { get; set; }
    }


    public class User:Base
    {
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Email { get; set; }
    }


    public   class Job:Base
    {
    [ForeignKey("Owner")]  //foreign keyi vardır. üzerine gelirseniz string name der. kimle ilişkilendirsin ad ı ister
    public int OwnerID { get; set; }
    public string Title { get; set; }
    public string Desription { get; set; }
    public DateTime expiredDate { get; set; }
    public string MinPrice { get; set; }
    public string MaxPrice { get; set; }
    public int Status { get; set; }


    //navigation property
    public virtual User Owner { get; set; }
    //sanal olarak oluşturcam,içindeki data da birşey olmasa da sorun yaşamıycam. o işin sahibini görmek istiyorsam owner properti üzerinden erişcem.


    //ya da
    //   public int OwnerID { get; set; } var

    // [ForeignKey("OwnerID")]
    // public virtual User Owner { get; set; } owner foreignkey olarak OwnerID ye aittir.
    }



    public  class Category : Base
    {

    public string Name { get; set; }
    public int? ParentID { get; set; } //kendi kendisini referans gösterir kendine döner.Üst Kategorisini vermiş oldul

    [ForeignKey("ParentID")]
    public virtual Category ParentCategory { get; set; }
    }








    Kullanıcı ve Rol arasındaki ilişki
    1 kullanıcının birden çok rolu olabilir.(admin,moderator,kullanıcı).1 rolün de binlerce kullanıcısı vardır.
    Kullanıcının birden fazla rolu olabilir aynı şekilde Rolun de birden fazla kullanıcı olabilir.
    EF versiyonunda 2 tabloyu birbirine liste olarak annotations bağladığımız zaman
    bir tabloda virtual Icollection User belirtip diğerinde Icolection Role dediğimizde
    EF bu tabloların arsındaki ilişkiyi anlayıp bizim için bu ara tabloyu sağlıyordu sanal olarak. Ama bu bir sorun oluşturuyordu.
    Özellikle biz bu dataları json a çevirmeye çalıştığım zaman, kullanıcıyı çağırıyoruz içinde o kullanıcı roleri geliyor, rolleri çağırıyoruz
    içinde kullanıcılar geliyor.Sürekli bir iç içe bir döngü oluşup çok fazla data çağırması gerekiyordu. Bu durumu hantallaştırıyordu.
    Bu durum ama EF Core ile kalktı, çoğul ilişki ara tablo kendisi oluşturmuyor, bizim yazmamızı istiyorlar.
    EF deki O hantal olarak tabir edilen performans da sona ermiş oldu.
    Ara tabloyu  EF Core da da kendimiz yaratacağız


    public class User:Base
    {
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Email { get; set; }

    //nav properties
    public virtual ICollection<Role> Roles { get; set; }
    }



    public  class Role: Base
    {
    public string Name { get; set; }

    //nav property
    public virtual ICollection<User> Users { get; set; } // rolun kullanıcılarına erişicez.Bağımlılığını azaltmak için ICollection dedik, List hashset desek sadece onları çağırabilecektik.
    }




    public class UserRole :Base
    {
    //tutmak istediğimiz 2 tür var kullanıcıID ve RoleID
    //bu tablo üzerinden Kullanıcı ve Role bilgiine de erişmek isteriz.
    //RoleID=1  KullanıcıID=10 geldiğinde , roleID1 inde kullanıcılarını görmek isteriz aynı şekilde kullanıcıID10 ununda biligilerini görmek isteriz.

    public int RoleID { get; set; }
    public Role Role { get; set; } //Role bilgisinide bu özellikle bilmiş olacağız rol türünden rol.

    public int UserID { get; set; }
    public User User { get; set; }


    }




    public  class Offer :Base
    {
    [ForeignKey("Job")]
    public int JobID { get; set; }
    [ForeignKey("Employee")]
    public int EmployeeID { get; set; }
    public long Price { get; set; }
    public string Desription { get; set; }

    //nag properties
    public virtual Job Job { get; set; }
    public virtual User Employee { get; set; }

    }




    public class Slide :Base
    {
    public string   ImageUrl { get; set; }
    public string Description { get; set; }
    public string Link { get; set; } //tıklayınca başka yere yönlendirecek
    public int ClickCount { get; set; }


    }





    public  class File : Base
    {
    [ForeignKey("Job")]
    public int JobID { get; set; }
    public int Url { get; set; }
    //nag properties
    public virtual Job Job { get; set; }
    }




    public  class Task: Base
    {
    [ForeignKey("Offer")]
    public int OfferID { get; set; }
    public double   Rate { get; set; }
    public int Status { get; set; }
    public DateTime DeadLine { get; set; }

    //nag properties
    public virtual Offer Offer { get; set; }
    }



    public class Message:Base
    {
    [ForeignKey("Owner")]
    public int OwnerID { get; set; }

    [ForeignKey("Employee")]
    public int EmployeeID { get; set; }

    [ForeignKey("Task")]
    public int TaskID { get; set; }
    public string Messages { get; set; }
    public bool IsRead { get; set; }

    //nav Properties
    public virtual Task Task { get; set; }
    public virtual User Owner { get; set; }
    public virtual User Employee{ get; set; }

    }

Dal Katmanında Migration oluşturalım.

Dal Katmanındaki ilk görevimiz:Veritabanı oluşturup Model Katmanında oluşturduğumuz modelleri, migration yardımıyla veritabanına eklemek.
EF ile Code First prosesini geliştireceğiz.
İlk görev Migration için bu katmana, birkaç kütüphane ekleyeceğiz. Migration esnasında bize yardımcı olacak araçlar.
Bağmlılıklar sağ click ve nuget paketlerini yönet tıklayalım.
ya da Araçlar->Nugget Paket Yöneticisini tıklayalım.
Yükleyeceğimiz paketler: Ef Core,Ef Core Design,Ef Core SqlServer, Ef Core Tools.

Dal Katmanında Ef adında bir klasör oluşturalım ve içerisine DeveloperContext adında sınıf ekleyelim.
Sınıfımız DbContext ten kalıtım alsın. override yazıp space tuşuna basalım, karşımıza metodlar gelecek.
OnConfiguring() ve OnModelCreating() metodlarını seçelim.

    protected override  void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
       optionsBuilder.UseSqlServer("server=.; Database=DeveloperDB; Integrated Security=True " ); 
    }
Not:eğer usesqlserver yazdığımızda server= / (slash) kullanmamız gerekirse , \\ iki ters slash koyarız.
Integrated Security=true -> Windows authentication ile giriş yapabilmeyi sağlar.
User authentication olsa, kullanıcı adı ve şifreyi yazmamız gerekecek, Integrated security ise false olacak

Bir alt satırına modellerimizi yazalım.
Veritabanında, Tablo olmasını istediğimiz tüm classları, DbSetin içinde parametre olarak vermemiz lazım.

        public DbSet<Category> Category { get; set; }  //DB deki DBSet tablo<Category türünde> Category adında {get;set;}
        public DbSet<File> File { get; set; }
        public DbSet<Job> Job { get; set; }
        public DbSet<Message> Message { get; set; }
        public DbSet<Offer> Offer { get; set; }
        public DbSet<Slide> Slide { get; set; }
        public DbSet<Task> Task { get; set; }
        public DbSet<User> User { get; set; }
        public DbSet<Role> Role { get; set; }
        public DbSet<UserRole> UserRole { get; set; }

Sıra onModelCreating() metodunda. Entity Framework, modellerin, ara tablolarunu otomatik oluşuyordu fakat Entity Framework Core da ise ara tablolarıı kendimiz oluşturmak zorundayız.

  
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
    modelBuilder.Entity<UserRole>
    ().HasKey(x => new { x.UserID, x.RoleID });
    // Entty arasına many to many tasarladığımız tablonun adını yazacağız.
    //Birinci. Ara tablomuzun, diğer iki tabloyla ilişkili olduğunu söylememiz gerek.
    //Aratablonun, 2 tane primary keyi varmış gibi HasKey ile belirttik.
    // 2 tane var yeni bir nesne yaratıp tablonun anahtarlarını göstereceğiz
    //public int RoleID { get; set; }  <-
    //public Role Role { get; set; }
    //public int UserID { get; set; }  <-
    //public User User { get; set; }


    modelBuilder.Entity<UserRole>().HasOne<User>(a => a.User).WithMany(b => b.Rolleri).HasForeignKey(c => c.UserID);
    //Şimdi tablolar arasındaki ilişkiyi teker teker söyleyeceğiz.
    //Aratablonun X tablosuyla Bire çok ilişkisini olduğunu söyleyeceğiz. Sonra da Y tablosuyla bire çok diyeceğiz.
    //Birinci User tablosuyla1 , roles ile çok diyelim.
    //aratablo bir ilişkiye (hasone) sahip  <User parametreli> a.User,(artık user modelide) user modelinde çok ilişkisi vardır  Rolleri propertisiyle. ve hasforign key userID diyeceğiz.

    modelBuilder.Entity<UserRole>().HasOne<Role>(a => a.Role).WithMany(b => b.Users).HasForeignKey(c => c.RoleID);
    //Yine bire-çok muş gibi davranıcaz. Role den bir çıkacak, user ise many.
    // HasOne<Role parametreli>(a => a.Role) (artık role modelindeyiz) Role modelindeki Users ile many ilişkilidir.

    }

DbContext sınıfımızı düzenlediğmize göre migration yapabiliriz.
Çözümü derleyelim.(Hatalar varsa düzeltelim)
Araçlar->Nugget Paket Yöneticisi->Paket Yöneticisi Konsolunu açalım.
Varsayılan Projenin Dal olduğuna dikkat edelim ve yazalım;
Add-migration isim(versiyon 1 gibi) , update-database -verbose (verbose ne var ne yoksa ekle demektir)

Migration esnasında yaşanan birkaç hatalar ve çözümleri

add-migration : The term add-migration is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
çözüm :Paketlerinizi yüklediğinizden emin olun, büyük olasılıkla EF Core Tools paketini yüklememişsinizdir.
Startup project Develope.Dal targets framework .NETStandard . There is no runtime associated with this framework, and projects targeting it cannot be executed directly. To use the Entity Framework Core Package Manager Console Tools with this project, add an executable project targeting .NET Framework or .NET Core that references this project, and set it as the startup project; or, update this project to cross-target .NET Framework or .NET Core. For more information on using the EF Core Tools with .NET Standard projects, see https://go.microsoft.com/fwlink/?linkid=2034705
Paket Yöneticisi konsol araçları, .NET Core veya .NET Framework projeleriyle çalışır. .NET Standard Sınıf kitaplığı (Dal katmanımız) ile Konsolda migration yaparken üstteki hata gelebilir. Bunun için bir deneme test konsol uygulaması ekleyelim ya da We projesi ekleyebiliriz. O projeyi, startapp project yapıp tekrar deneyelim.
Başka bir seçenek olarak Web.Ui katmanımıza gidip, Startup.cs dosyasına şu komutları girbilriz.

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(x => x.EnableEndpointRouting = false);
            services.AddDbContext<DeveloperContext>(); //startup dışında hiçbiryerde tanıtmıycaz.
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        { 

            using (var scope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                using (var context = scope.ServiceProvider.GetService<DeveloperContext>())
                {
                    //context.Database.EnsureDeleted(); //sildiğinden emin ol eğer varsa sil yarat.
                    //context.Database.EnsureCreated();  //yaratıldığından eminol.
                    context.Database.Migrate(); // yada b komutla bizim için bir veritabanı tasarlamış olucak.DB yarat, migrate et komutudur.
                }
            } 
    }

Dal Katmanında abstract ve concrete adında 2 klasör oluşturalım.
Abstract klasöründe soyut sınıfımız(metodların imzası), Concrete klasöründe ise somut sınıflarımız olacak.(metodların gövdesi)
Abstract klasöründe, Modellerimizin interface repositoryleri olacak ve hepsi Core katmanındaki Generic yapılı IRepositoryde kalıtım alacak.
Aynı şekilde Concrete klasöründe, modellerimizin repositoryleri olacak ve hepsi Core katmanındaki Generic yapılı BaseRepositoryden kalıtım alacak.

Abstrct klasörümüzde ICategoryRepository interface oluşturalım ve IRepository den kalıtım alsın.

    public  interface ICategoryRepository : IRepository<Category>
    {
    }

Concrete klasörümüzde CategoryRepository sınıfı oluşturalım ve ilk BaseRepositorymizden daha sonra ICategoryRepositoryden kalıtm alsın.

  public  class CategoryRepository : EFRepository<Category, DeveloperContext>,ICategoryRepository
    {
        public CategoryRepository(DeveloperContext prm):base(prm)
        {
        }
    }

Not: Base modelinizin :IData dan kalıtım aldığına emin olun.

Dal Katmanında isteidğimiz veriyi, istediğimiz işlemi, istediğimiz ekstra metodlarıyle işledik.
Dal da tüm işlemler serbest iken, Bll katmanında bu serbestliği bozacağız.
Bll de yetkilere ve işlemlere sınır koyulacak.Web.Ui ile iletişim halinde olacak ve web, bll katmanının sınırlı kurallarından yararlanabilecek.
Bll de yetkiler kısıtlanacak, bu katmanda sadece projeye giren kullanıcıların yapmak istedikleri metodlar, hazırlanacak.
örneğin, Kullanıcı, güncelleme, ekleme yapsın ama silme işlemi yapamasın. Amacımız bu mantıktır.
Bll de Abstract ve Conrete klasörler olacak.Repository sonlarına Manager,Service gibi ekler ekleyebiliriz. Ya da size kalmış.

Bll Katmanının Abstract klasöründe IOfferService adında interface oluşturalım.

   //Sadece kullanmak istediğimiz metodları yazıyoruz.
   public  interface IOfferService
    {
        ICollection<Offer> GetAllByJobID(int jobID);  //İş Id sine göre tekllif getirelim.
        ICollection<Offer> GetAllByEmployee(int empID);
        bool Add(Offer data);
        bool Update(Offer data);
        bool SoftDelete(int id);
        Offer Get(int id);
    } 

Bll Katmanının Concrete klasöründe OfferService adında sınıf oluşturalım.
Crud işlemleini Dal Katmanında yapmıştık. O halde ordan constructor ile bağımlılık alalım ve nesnelere erişelim.
Unutmayalım ki Dal daki metotları istifade ederken biz sadece kendi Bll deki interface imzalarına göre erişim sağlayacağız.
Not:Dependency Injection da, nesnenin kendisini değil kuralını (interfaceni) bağımlılık alırız.

 
namespace Developer.Bll.Concrete
{
    public class OfferService : IOfferService
    {
        IOfferRepository _offerRepo;
        public OfferService(IOfferRepository prm)
        {
            _offerRepo = prm;
        }
        public bool Add(Offer data)
        {
            var result = _offerRepo.Add(data);
            return result.Status ? true : false;
        }

        public Offer Get(int id)
        {
            var result= _offerRepo.Get(x => x.ID == id);
            return result.Data;
        }

        public ICollection<Offer> GetAllByEmployee(int empID)
        {
            return _offerRepo.GetAll(x => x.EmployeeID == empID).Data;
        }

        public ICollection<Offer> GetAllByJobID(int jobID)
        {
            return _offerRepo.GetAll(x => x.JobID == jobID).Data;
        }

        public bool SoftDelete(int id)
        {
            Offer data = _offerRepo.Get(x => x.ID == id).Data;
            data.IsActive = false;
            data.IsDeleted = true;
            return _offerRepo.Update(data).Status;
        }

        public bool Update(Offer data)
        {
            return _offerRepo.Update(data).Status;
        }
    }
}




Projemiz, MVC katman yöntemine dayanacaktır.

Startup.cs Dosyası


Startup.cs dosyasını yapılandıralım, projemiz MVC yapısında olacaktır.
Servis Konfigür metoduna -> service.AddMvc() ekleyelim
Konfigüre metoduna -> app.useMVC( route açıklayalım) ekleyelim. Durum kodlarına ve static Files oluşumunu sağlayalım.
Not: AspNet Core Versiyonlara göre route değişiklikleri gösterebilir. Eğer varsayılan projeiz, endpoints route'u desteklemişse, MvcDeafaultRouting ile değiştereceğiz, projemizi mvc ile geliştireceğiz.

   public void ConfigureServices(IServiceCollection services)
        {
            //Mvc route kullanacaksak, default EndpointRouting yönlendirmeyi pasif'e çevimeliyiz.
            services.AddMvc(option => option.EnableEndpointRouting = false);
            services.AddControllers(options => options.EnableEndpointRouting = false);
            services.AddControllersWithViews(options => options.EnableEndpointRouting = false);
            services.AddRazorPages().AddMvcOptions(options => options.EnableEndpointRouting = false);
 
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //2 şekilde MVC yöntemini kullanacağımızı belirtebiliriz. ya varsayılan ayarı getir deriz ya da kendimiz yazarız.
            //1.Şekil
            //app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}"  (controller=Home, diyerek default Home dur demek isteriz)
                    );

            //});
            //2.Şekil varsayılan halini kullanacağımızı bellirtiriz.
            app.UseMvcWithDefaultRoute();  
 

            //Birkaç faydalı Konfigür ayarları vardır. Bunlar;

            //env.IsDevelopment Metodu;Proje geliştirdiğimizde hataları gösterir,yayınlandığında hata görünmez,exception sayfalar gösterilir.
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();  
            }

            //UseStaticFiles() metoduyla, statik dosyalarımızı Asp.Net Core ile gösterebiliriz. Varsayılan klasör; wwwRoot'tur.
             app.UseStaticFiles();   


            //UseStatusCodePages() metoduyla; hata durum sayfaları, ufak bir mesaj olarak sayfada belirir.
             app.UseStatusCodePages();
        }

  

Program.cs, projemizin beynidir. Bu dosya ile proje ayağa kalkar. Webhosting oluşur.Server türü kestreldir.Proje derlenip çalışır.Burdaki ayarlara, bu proje için dokunmayacağız.

MVC klasörlerini ve Varsayılan gereklilikleri oluşturalım

Uygun klasörleri oluşturalım. Models,Controllers,Views ve wwwroot klasörü.(wwwroot klasörü Asp.Net Core da gösterilen tek klasördür.Adnı Startup.cs ile değiştirebiliriz.Lakin pek gerek yoktur.)
Views klasöründe 2 adet view dosyası oluşturalım.
(a) _ViewImports , içeriğine;
    _ViewImports sayfası, bizim import edeceğimiz dosyaları belirler.
    @namespace App.Web.Models //proje adı ve Models klasörümüzü tanıttık.
    @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers //Taghelper namespace'ini tanıttık.(Tüm sayfalara etkilenmiş oldu.)
    
    (b)  _ViewStart  , içeriğine    
       Proje çalıştığında sayfaları etkileyecek stil, webmaster sayfası,title burda tanımlanır.
    @{     ViewData["Title"] = "_ViewStart";  Layout = "_Layout";     }

    (c)Bu sefer bir klasör oluşturup içerisine view ekleyelim.Shared klasörü, _Layout view dosyası ekleyelim.
     Asp.Net WebForms daki web master sayfamızdır.Tüm sayfalar bu anasayfadan istediğimizde etkilenebilecek.
    @{  Layout = "~/Views/Shared/_Layout.cshtml"; }   
Controllers klasörüne, HomeController ekleyelim.Çıkan pencerede Layout kullanacağımızı sekmeye tıklayarak belirtelim.Bir index sayfası oluşturalım.(Not:Ekleme sırasında, proje otomatik olarak paketler yükleyebilir.efcore,Tools,Logging,Design vsb.)


(4) Projeye sağ click, istemci Tarafı Kitaplığını tıklyalım.Dilediğimiz css ve js yapılarını, bu araçla projemize indirelim.
Sağlayıcı=> Nugget kütüphanesi olsun (unpkg kısaltılmasıdır)
Arama paneline; "bootstrap css" yazıp ve wwwroot klasörü içerisinde oluşturucağımızı belirtelim.
Aynı araçla, "font awesome css", "Jquery" gibi dilediğimiz kütüphaneleri indirebiliriz.
Projemize bir template giydireceksek, template'i direk sürükle bırak yöntemi ile -wwwRoot klasörü içerisine atalım. css,js,images adında klasör oluşturalım. İstediğimiz dosyayı kopyalayıp, customs oluşturduğumuz klasör'e yapıştırabiliriz.Templates deki images klasörünü , wwwwroot klasörüne images adnda klasör oluşturup, kopyala/yapıştır yapalım.

Not:AspNet Core, proje içerisindeki herhangi custom yarattığınız klasörleri tanımaz. Url de bildirseniz bile size o klasörü içindeki herhangi bir dosyayı vermez.Tanıdığı klasörler wwwRoot içerisinde olmalıdır. ya da tanıtmak için startup.cs de staticfiles metodunda belirtmeniz gerekir.

Bundler ve Minifier aracını kullanmak istersek; https://marketplace.visualstudio.com/items?itemName=MadsKristensen.BundlerMinifier sitesini ziyaret edip bundler ve minifer özelliğini indirelim.Projeyi resetleyip açtığımızda artık css ya da js dosylarına sağ tıkladığımızda add bundler&minifier adında bir özellik gelecektir.İstediğiniz css ler'e ctrl ile seçip add bundler&minifier özelliğini tıklatarak wwwroot-> css klasöründe birleştirebiliriz.Burda Dikkat etmemiz gereken bir husus vardır. Templateki ilgili css ve ya jslerin üzerine ctrl seçerken, sırasına dikkat etmeliyiz.Aksi takdirde çakışmalar yaranır. Sağ click add bundler&minimizer özelliğini kullanıp wwwroot içerisindeki css klasörümüze bir dosya oluştrup birleştirelim.Bu dosyayı incelediğimizde,sırasının doğru olduğunu kontrol edelim,değilse düzenleyelim.

Yönetim işlerini, Web.Ui katmanında farklı bir alanda yaparız.
Add-Area ile katmanımıza yeni bir alan oluşturalm. Eğer Add-Area sekmesi yoksa kendimiz elle oluşturalım.
Areas - Admin ve içerisinde Models,Views,Controllers
Controllers da bir HomeController denetleyicisi oluşturalım.
Views katmanında Shared klasörü oluşturalım.İçerisine _Layout view ekleyelim ve normal bir html komutlarını içerisine girelim.
https://athemes.com/collections/free-bootstrap-admin-templates/ sitesinden star admini indirip tasarım oluşturalım.
Tasarım taslağını Admin dizininde, temp adında bir klasöre kopyalayıp yapıştıralım.
Admin alanında Controlersda HomeController ve Index sayfası oluşturalım. Projeyi başlatınca hata alırız çünkü iki HomeController varve startup.cs karıştırır.Bundan dolayı iki alternatifimiz var;
A-ya HomeController'a gelip başına [Area("Admin")] imleci koyarız.
B- ya da AdminBaseController adında bir controller oluşturur.Oraya [Area("Admin")] koyup diğer controller sınıflarını AdminBaseControllerdan türetiriz.

     [Area("Admin")]
    public class AdminBaseController : Controller
    {
    }

HomeContreollera da : AdminBaseden türediğni yazarız. Böyllikle    [Area("Admin")] 1 kere kullamış oluruz.
    public class HomeController : AdminBaseController
    {
        public IActionResult Index()
        {
            return View();
        }
    }

Startup.cs, configure metoduna girip ilk areas sonra default routing yapısını oluşturalım. İleride çatışma yaşamayalım.

            app.UseMvc(x =>
           {
              x.MapRoute( "areas", "{area:exists}/{controller}/{action}/{id?}", new {controller="Home",Action="Index" });//varsa çatışmasın
              x.MapRoute("default", "{controller}/{action}/{id?}", new { controller = "Home", Action = "Index" }); 
           });  



Dependency Injection (Bağımlılık Enjeksiyonu)
Martin Fowler'ın geliştirdiği bir prensipdir.
Dependency Injection (Bağımlılık Enjeksiyonu),bağımlı parçaların birbirinden ayrılıp bunların dışarıdan enjekte edilmesi ve böylelikle sistemdeki bağımlılıkların azaltılmasıdır. Aradaki ilişkiyi gevşek tutmak, ilerde yapacağınız değişiklikler açısından önemlidir.
Örneğin, A sınıfında, B sınıfı nesnesi üretildi.Böylelikle A , B'ye bağlı hale geldi.İlerde B sınıfında bir değişiklik yaparsanz, A sınıfında da yapmanız gerekir.İşte bu bağımlılığı olabildiğince minimuma indirgeyeceğiz.
//Hatalı yaklaşım
public class Student
{
   private DbFacade facade;
   public Student()
   {
    facade = new DbFacade();
   }
 ....
}

Dependency Inversion prensibine göre;

(a)Modüller birbirine bağlı olmamalı. Her ikisi de soyut (abstract) kavramlara bağlı olmalıdır. Bağımlılıklar, soyutlaştırılmalıdır.Bunun için interface veya abstract class kullanmalıyız.Interface ve ya abstract kullanarak sınıflar arasında geçiş yapmalıyız. Böylece sürdürülebilir bir yazılım projesi geliştirmiş oluruz.
(b)Inversion of Control(IoC) prensibine göre bağımlılıkları oluşturmanın kontrölü, dışarıdan olmalıdır. Dependency Injection, IoC prensibini implement eden bir design patern olduğu için bağımlılıklar class’a dışarıdan “enjekte” edilir. Kullandığımız sınıfta, farklı bir sınıfın nesnesini kullanıcaksk new anahtar sözcüğünü kullanmayız. Bu nesneyi Contructor ya da Setter metoduyla, parametre olarak alırız.
public class Student
{
  private IdbFacade _facade;
  public Student(DbFacade prm)
  {
   this._facade = prm;
  }
  ....
}

Hatırlatma

Hatırlarsak; Core Katmanında concrete klasöründe metodların gövdelerini oluşturuyorduk.
context nesnesi oluşturup durduk sonra using ile dispose ettik.Son scope, kapandığında dispose oluyordu ram de yer kaplamıyordu ve işimiz bitiyordu.
using ile context bağımlılığını indirgemek için constructor da bir kere TConext nesnesi oluşturduk ve her elemanda kullandık.

 public class EFRepository<T, TContext> : IRepository<T> where T : class, IData, new() where TContext : DbContext, new()
    {
        readonly TContext ctx; 
        public EFRepository(TContext prm) 
        {
            ctx = prm;
        }
        public E_Bildiri<T> Add(T prm)
        {
                var data = ctx.Entry(prm);
                data.State = EntityState.Added;
                //TernaryIF ile

                return ctx.SaveChanges() > 0 ? new E_Bildiri<T> { Data = prm, Bildiri = "başarılı", Status = true } : new E_Bildiri<T> { Bildiri = "hata", Status = false };
        }

Dal Katmanında Concrete klasöründe de EfRepository den kalıtım aldık ve burda da constructor oluşturup base deki context kullanılmasını belirttik.

namespace Develope.Dal.Concrete
{
  public  class CategoryRepository : EFRepository<Category, DeveloperContext>,ICategoryRepository
    {
        public CategoryRepository(DeveloperContext prm):base(prm)
        {

        }
    }
}


Şimdi kısa bir Slide içerği oluşturup servisii çağıralım.

        ISlideService _slideservice;
        public HomeController(ISlideService prm)
        {
            _slideservice = prm;
        }

        public IActionResult Index()
        {
            return View(_slideservice.GetAll());
        }
        public void ConfigureServices(IServiceCollection services)
        { 
            services.AddDbContext<DeveloperContext>(); //startup dışında hiçbiryerde tanıtmıycaz.
            services.AddScoped<ISlideService, SlideService>();
            services.AddScoped<ISlideRepository, SlideRepository>();
        }

@model ICollection<Developer.Model.Slide>

@foreach (var item in Model)
{
    @item.ImageUrl
    @:
    @item.Description <br />
}

Servislerimize bakarsak birden çok interfaceleri ve sınıfları var.Controller da bunları çağırırken constructor oluşturuyor ve bu controllerın elamanları, oluşturduğumuz IServis nesnesinden yararlansın, içi bu parametreyle dolsun diyoruz.Birden çok olduğu için bir sürü IServis kullanmış oluruz.Bunun için UnitOfWork kullanalım. herbirini tek tek kullanmak yerine, bunların hepsini bir yere bağlayalım.Bir paket olarak, kullanıcaya sunalım.
Birden fazla servisi bir arada tutacak bir konteyner servisi hazırlayalım ve adını IUnitOfWork imzasında gösterelim.
Not:DotNetin dispose metoduvar.IDisposable interface inden gelir. Bunu da kullanalım.

     //Bll katmanında UnitofWork adında bir klasör oluşturalım ve içerisine IUnitOfWork adında bir interface ekleyelim.
namespace Developer.Bll.UnitOfWork
{
   public interface IUnitOfWork:IDisposable
    {
        ICategoryService Category { get; }
        IFileService File { get; }
        IJobService Job { get; }
        IMessageService Message { get; }
        IOfferService Offer { get; }
        IRoleService Role { get; }
        ISlideService Slide { get; }
        ITaskService Task { get; }
        IUserService User { get; } 
        int Save{get;} //Bu kendimizin SaveChanges metodu olacak.
    }
}




    Interfaceleri ya constructo yöntemi ile ya da encapsulation yöntemi ile çağırabilirsiniz. New instance alınmaz.
Encapsulationda ilk önce field oluşturusun sonra property oluşturursun.
Kapsülleme (Encapsulation) sayesinde nesneler bilinçsiz kullanımdan korunmuş olur. Fakat bazı durumlarda private field’ lara erişmemiz ve özelliklerini kullanmamız gerekebilir. Bu durumda Property kavramı devreye girer. Property bir field’ in değerini geri döndürmeye (Get) ve değerini ayarlamaya (Set) olanak sağlar. Constructor ile
ilk önce IOfferRepository değişenini oluşturalım.
Sonra constructor oluşturup bize bunu göndermesini istiycez.

Buraa birde Save metodu ekleyelim.
EFRepository de sürekli savechanges() yapıyoruz.Ekleme güncelleme silme.Bir ilan ekledik, kategori ekledik,fotograf ekledik.Kategori servisii çağırdık add metodunu kulladık , add metodunda ekledi geldi, Fotograf servisi çağırdık add metodunu kullandık SaveChanges etti geldi ve ynı şekilde ilan içinde. Bunun yerine .Kullanıcı,Ekleme işlemini lokalde yapsın, ben unitofworkda savechanges() çağırdığımızda hepsini birden eklesin. EF dibine kadar verimli kullanmış oluruz.

O zaman EfRepository ye gidip SaveChanges() leri kaldıralım.

//repository de yazdığımız medotları seçip ctrl Kastamonu Sivas yapalım ve try seçelim.(ctrl K ctrl S)
 public class EFRepository<T, TContext> : IRepository<T> where T : class, IData, new() where TContext : DbContext, new()
    {
        readonly TContext ctx; //bu nesne içi boş
        //bu EFRepository yi kulanan bütün contextler bu elemanı kullansın.bir parametre vererek bunun içini doldursun.
        public EFRepository(TContext prm) 
        {
            ctx = prm;
        }
        public E_Bildiri<T> Add(T prm)
        {
            try
            {
                var data = ctx.Entry(prm);
                data.State = EntityState.Added;
                //TernaryIF ile

                //return ctx.SaveChanges() > 0 ? new E_Bildiri<T> { Data = prm, Bildiri = "başarılı", Status = true } : new E_Bildiri<T> { Bildiri = "hata", Status = false };
                return new E_Bildiri<T> { Data = prm, Bildiri = "başarılı", Status = true };
            }
            catch (Exception ex)
            {

              return  new E_Bildiri<T> { Bildiri = ex.Message, Status = false, Data=prm };
            }
        }

        public E_Bildiri<T> AddAll(List<T> prm)
        {
            try
            {
                ctx.AddRange(prm); //tek data ile olan metod işimize yaramaz. void olarak geri döner zaten değişkenede atayız
                                   //return ctx.SaveChanges() > 0 ? new E_Bildiri<T> { Bildiri = "başarılı", Status = true } : new E_Bildiri<T> { Bildiri = "hata", Status = false };
                return new E_Bildiri<T> { Bildiri = "başarılı", Status = true };
            }
            catch (Exception ex)
            {
                return new E_Bildiri<T> { Bildiri = ex.Message, Status = false };
            }
        }

        public E_Bildiri<T> Delete(int ID)
        {
            try
            {
                    var data = ctx.Set<T>().Find(ID);
                    var deletedData = ctx.Entry(data);
                    deletedData.State = EntityState.Deleted;
                //int result = ctx.SaveChanges();
                ////ternary if
                //return result == 0 ? new E_Bildiri<T> { Status = false, Data = null, Bildiri = "Kayıt silinemedi." } : new E_Bildiri<T> { Status = true, Data = data, Bildiri = "Kayıt silindi." };
                return new E_Bildiri<T> { Status = true, Data = data, Bildiri = "Kayıt silindi." };
            }
            catch (Exception ex)
            {
                return new E_Bildiri<T> { Status = false, Data = null, Bildiri = ex.Message };
            }
        }

        public E_Bildiri<T> Get(Expression<Func<T, bool>> filter)
        {
                var sonuc = ctx.Find<T>(filter); //Find<>(expresion bekler bizde filter ekledik)bilgiyi yakaladık.
                return new E_Bildiri<T> { Bildiri = "Getirildi", Data = sonuc, Status = true };
        }

        public E_Bildiri<ICollection<T>> GetAll(Expression<Func<T, bool>> filter = null)
        {
                List<T> sonuc;
                if (filter == null)
                {
                    sonuc = ctx.Set<T>().ToList();  // !using System Linq
                }
                else
                {
                    sonuc = ctx.Set<T>().Where(filter).ToList();  // !using System Linq
                }
                return new E_Bildiri<ICollection<T>> { Data = sonuc, Status = true, Bildiri = "data listelendi" };
        }

        public E_Bildiri<T> SoftDelete(int ID)
        {
            try
            {
                    var data = ctx.Set<T>().Find(ID);
                    var deletedata = ctx.Entry(data);

                    deletedata.State = EntityState.Modified;
                //return ctx.SaveChanges() > 0 ? new E_Bildiri<T> { Bildiri = "güncellendi", Data = data, Status = true } : new E_Bildiri<T> { Bildiri = "hata", Data = null, Status = false };
                return new E_Bildiri<T> { Bildiri = "güncellendi", Data = data, Status = true };
            }
            catch (Exception ex)
            {

                return new E_Bildiri<T> { Bildiri = ex.Message, Data = null, Status = false };
            }
        }

        public E_Bildiri<T> Update(T prm)
        {
            try
            {
                    var data = ctx.Entry(prm);
                    data.State = EntityState.Modified;

                //return ctx.SaveChanges() > 0 ? new E_Bildiri<T> { Bildiri = "güncellendi", Data = prm, Status = true } : new E_Bildiri<T> { Bildiri = "hata", Data = null, Status = false };
                return new E_Bildiri<T> { Bildiri = "güncellendi", Data = prm, Status = true };
            }
            catch (Exception ex)
            {

                return new E_Bildiri<T> { Bildiri = ex.Message, Data = null, Status = false };
            }
        }
    }



artık SaveChanges() metodunu kendi IUnitOfWrk adlandırmamızla yapabileeğiz.
Şimdi de UnitOfWork sınıfını oluşturalım ve IUnitOfWorktan kalıtım alsın
public class UnitOfWork : IUnitOfWork arabirimi uygula derken dispose düzenleyerek implemente etsin diyelim.

    public class UnitOfWork : IUnitOfWork
    {
        protected DeveloperContext context;
        //Constructor ile nesneyi doldurucaz.UnitOfWork oluşturulduğu anda benim bu nesnem, DevelperContext olarak dolacak.
        public UnitOfWork(DeveloperContext prm)
        {
            context = prm;
        }
        //Encapsulation yapıcazğı.İlk önce fieldlerini hazırlıyorum.
       
        ICategoryService _cat;
        IFileService _file;
        IJobService _job;
        IMessageService _msg;
        IOfferService _offer;
        IRoleService _role;
        ISlideService _slide;
        ITaskService _task;
        IUserService _user;
        //Bir field içi doluysa onu doldur, değilse yeni bir servis yarat.
        //?? eğer yoksa şunu yap demektir.Category => cat eğer dolu değilse ??yeni bir CatServis yarat, o da bir repositor ister içine bakabilirsin.
        public ICategoryService Category => _cat ?? (_cat = new CategoryService(new CategoryRepository(context)));

        public IFileService File => _file ?? (_file = new FileService(new FileRepository(context)));

        public IJobService Job => _job ?? (_job = new JobService(new JobRepository(context)));

        public IMessageService Message => _msg ?? (_msg = new MessageService(new MessageRepository(context)));

        public IOfferService Offer => _offer ?? (_offer = new OfferService(new OfferRepository(context)));

        public IRoleService Role => _role ?? (_role = new RoleService(new RoleRepository(context)));

        public ISlideService Slide => _slide ?? (_slide = new SlideService(new SlideRepository(context)));

        public ITaskService Task => _task ?? (_task = new TaskService(new TaskRepository(context)));

        public IUserService User => _user ?? (_user = new UserService(new UserRepository(context)));

        public int Save => context.SaveChanges();

Artık Startup.cs de Tabloservisler yerine services.AddScoped<IUnitOfWork, UnitOfWork>(); diyeceğiz.
Hadi bir deneme yapalım.

        IUnitOfWork uow;
        public HomeController(IUnitOfWork prm)
        {
            uow = prm;
        }
        public IActionResult Index()
        {
            return View(uow.Slide.GetAll().ToList());
        }



ViewModel sınıfı oluşturup bir ekleme işlemi yapacağız.
Controller sınıfımızda bir metot oluşturacağız, ViewModel tipli modeli alacağız ve metodun gövdesinde Slide Model ile eşleştireceğiz. Context'e ekleyip geri döndüreceğiz.
Hatırlarsak; AspNet WebForms da; elementler oluşturup o elementlere Runat=server atributeları veriyorduk.Bu elementlerin Id leri üzerinden, cs tarafında elementlere erişip içlerini dolduruyorduk.
Lakin AspNet MVC de, ön yüz ve arka yüzü tamamen birbirinden ayrılmıştır. Biz bir şekilde iletişimi sağlıyoruz.
Bunun için; ön yüz cshtml sayfalarında bulunan html elementlerin name attribute ları ile model de gönderdiğimiz bilgileri eşleştiriyoruz.İletişim name attributelar sayesinde sağlanıyor.

Html Helper Kolay Kullanım
MVC ile birlikte Html helperlar da geldi. Onların sayesinde artık kodlara Html etiketleri yazmak zorunda değiliz.
Html Helper kütüphanesi ile html classı çağırıyoruz ve onlar bizim için html etiketi oluşturuyor.
Bu kütüphaneye göz gezdirmemizde büyük fayda var. Visual Studio programımızda kontrolleri incelemek için @Html yazıp ilgili kod metotlarını görebiliriz .
Tag Helper Daha Kolay Kullanım
Html Tag Metodların içinde de overloadlar vardır. Bu biraz zaman kaybettirebiliyor. Bundan dolayı bundan daha kolay şekilde yararlanmak için, TagHelper lar oluştu.
HtmlHelper'ı geliştirdiler ve TagHelper oluşturdular. Daha okunalı, yazılımı daha kolay. Tag Helperların, standart elementlerden tek farkı, asp için çalıştırdıklarını gösteren asp-for etiketidir.
Çalışması içinde mutlaka _ViewImports sayfasına aşağıdaki kodu yazmamız gerekir.

     @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
    
Model katmanımızda bir slideviewmodel adnda bir model oluşturalım. 

namespace Developer.Model
{
    class SlideViewModel
    {
        public int ID { get; set; }
        public string Link { get; set; }
        public string ImageUrl { get; set; }
        public string Description { get; set; } 
    }
}

Areas Admin klasörüne gelip Controller sağ ekleyip SlideController ekleyelim.Ekleme metodu yazalım.(ViewModel kullanacağız))

public IActionResult List()
{
    var slide = uow.Slide.GetAll();
    return View(slide);
}
         

 @model ICollection<Developer.Model.Slide>

@foreach (var item in Model)
{
    @item.ImageUrl 
    @: 
    @item.Description
<a asp-action="Edit" asp-controller="Slide" asp-route-id="@item.ID">Düzenle</a> }


public IActionResult Add()
{
    return View();
}
[HttpPost]
public IActionResult Add(SlideViewModel prm)
{
    Slide slide = new Slide();
    slide.Description = prm.Description;
    slide.ImageUrl = prm.ImageUrl;
    slide.Link = prm.Link;
    slide.ClickCount = 0;
    slide.InsertDate = DateTime.Now;
    slide.IsActive = true;
    slide.IsDeleted = false;

    //bool Add(Slide data) bool metodunda
    int result = uow.Slide.Add(slide) ? uow.Save : 0;
    return result > 0 ? RedirectToAction("List") : RedirectToAction(nameof(Add));
        
}
@model Developer.Model.Slide
 
 

<form asp-controller="Slide" asp-action="Add" method="post">

<div class="form-group">
    <label asp-for="ImageUrl" ></label>
    <input asp-for="ImageUrl"  class="form-control" >
</div>
      
<div class="form-group">
    <label asp-for="Link"></label>
    <input asp-for="Link" class="form-control" >
</div>

     

<div class="form-group">
    <label asp-for="Description"></label>
    <input asp-for="Description" class="form-control" >
</div>

     
<button type="submit"  class="btn btn-primary">Submit</button>
</form>  


public IActionResult Edit(int id)
{
    Slide gunceldata = uow.Slide.GetAllBySlide(id).First();
    SlideViewModel slide = new SlideViewModel
    {
        ID = gunceldata.ID,
        Description = gunceldata.Description,
        ImageUrl = gunceldata.ImageUrl,
        Link = gunceldata.Link
    };
    return View(slide);
}
    
 @model Developer.Model.SlideViewModel


<form asp-controller="Slide" asp-action="Edit" method="post">

<div class="form-group">
    <label asp-for="ImageUrl"></label>
    <input asp-for="ImageUrl" class="form-control" >
</div>
      
<div class="form-group">
    <label asp-for="Link"></label>
    <input asp-for="Link" class="form-control" >
</div>


<div class="form-group">
    <label asp-for="Description"></label>
    <input asp-for="Description" class="form-control" >
</div>




<button type="submit" class="btn btn-primary">Submit</button>
</form> 

@Html.ActionLink("Anasayfa", "Index", "Slide", new { Area = "Admin" }, new { @clas = "btn btn-primary" });

Öncelikle _ViewImports sayfamıza gelip TagHelper eklediğimizden emin olalım.
Admin işlemleri göreceğimiz için, Areas-Admin-Views klasörü içerisinde _ViewImports adlı sayfa ekleyeceğiz.
TagHelpersın içindeki * işareti herşey demektir.

@using Developer.Ui
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers   
@addTagHelper *, Developer.Ui

Admin-Controller klasörüne bir adet CategoryController oluşturalım.
Add Metodu yazacağız. Result Action metodu oluşturup Add adını verelim.Sağ tıklayıp Add View diyelim gelen pencereden Category Modelini seçip bize otomatik bir Add sayfası oluşturalım.Tag Helper yapısını otomatik oluşturduğunu görelim.

Tag Helper yapısını gördükten sonra Kategoriyle ilgili ekleme işlemine başlayalım.
Category Modelimizde bir çok property var biz kısalaştırma adına ViewModel oluşturalım.Maksat kısa ve öz işleyiş.

   public class CategoryVM
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int ParentID { get; set; }
    }
 [Area("Admin")]
    public class CategoryController : Controller
    {
        readonly IUnitOfWork uow;
        public CategoryController(IUnitOfWork prm)
        {
            uow = prm;
        }
        public IActionResult Index()
        {
            return View();
        }
        [HttpGet]
        public IActionResult Add()
        {
            ViewBag.Categories = uow.Category.GetAll();
            return View();
        }
        [HttpPost]
        public IActionResult Add(CategoryVM prm)
        {
            uow.Category.Add(new Model.Category
            {
                InsertDate = DateTime.Now,
                IsActive = true,
                IsDeleted = false,
                Name = prm.Name,
                ParentID = prm.ParentID
            });

            int result = uow.Save;
            TempData["message"] = result > 0 ? "Data Saved" : "No data fetched";
            return View(nameof(Add));

        }
namespace Developer.Ui.Models.CustomTagHelper
{
    [HtmlTargetElement("my-dropdown")]
    public class SelectListTagHelper:TagHelper
    {
        [HtmlAttributeName("list-for")]
        public List<Category> ListCategory { get; set; }
        //Bu metodla hangi html çıktısı vermek istediğimiz söyleriz.


        public ModelExpression AspFor { get; set; }  //Extra olarak modelexpresin tipinde aspforaldık.
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName = "select";
            output.TagMode = TagMode.StartTagAndEndTag;
            var sb = new StringBuilder();

            //Extra aspfor boşgelmez ise atributelerine ekle. 
            if (!String.IsNullOrEmpty(this.AspFor.Name))
            {
                output.Attributes.Add("name", this.AspFor.Name);
            }
            this.ListCategory.ForEach(x => sb.AppendFormat("<option value='" + x.ID + "'>" + x.Name + "</option>"));
            output.PreContent.SetHtmlContent(sb.ToString());
            base.Process(context, output);  
        }
    }
}
 //Add Sayfamız

@model Developer.Model.ViewModels.CategoryVM
@using Developer.Model


<div class="row">
    <div class="col-md-4">
        <form asp-action="Add">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ParentID" class="control-label"></label> 
                @Html.DropDownListFor(x => x.ParentID, new SelectList(ViewBag.Categories as List<Category>, "ID", "Name", "Seçiniz"))
                @*<select asp-for="ParentID" asp-items="ViewBag.Categories" class="form-control" ></select>*@
                <my-dropdown asp-for="ParentID" list-for="@ViewBag.Categories as List<Category>"></my-dropdown>
                <span asp-validation-for="ParentID" class="text-danger"></span>
            </div>

           
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>



Partial View ve Componentler yardımıyla, Kategori Listeleyeceğiz. Shared paneline veritabanından çektiğimiz bilgileri göstermek istiyoruz.
WebFormlarda .cs vardı ve backendde kodlarımızı çalıştırıyorduk.
Fakat asp.Net MVC design patternde, layout panelinde küçük bir kod bloğu çalıştırıp orda göstermesini isteyeceğiz.
Bunun için .Net MVC ile Partial View kod blogları oluşturuldu.
Partial View, Bir sayfanın içinde çağrılan bir kod bloğudur. İstediğiniz alanda çağırabileceğiz.Birden fazla olabilir.
View ile çağrıldığı, aynı klasörün içinde ya da shared klasörü içerisinde yazılmalıdır Çağırmak için @Html.Partial() içerisine partial sayfanın adı girilir.örn;KategoriPartial.
Bu metot ilgili isimli partial'ı bulup kodbloğunu ekliyor.
Asp.Net MVC de, Html.Action ise çağırdığımız bir isimle action çalıştırırdık. Html.Action lar performans düşüklüğüne neden oluyordu.Bundan dolayı Asp.Net Core da Html.Action lar yok sadece partiallar var.

@Html.Partial("CategoryPartial")
@Html.Partial("CategoryPartial", ViewBag.CatListe as ICollection<CategoryModel>) viewbag eklemek istersek model türünüde ister.

Componentler

Componentler partial view gibidir, yalnızca daha esnektir. Partial Viewler, dışardan bir veri alıp içeriğini, istenilen tüm viewlerde gösterebilen bir yapı olabilir ama Partial View'in modeli, yerleşeceği view'e özgü, bağımlı bir modeldir. Dolayısıyla uyum sağlaması şarttır.
Componentlerin, yerleşeceği view'in modeline, uyum sağlama zorunluluğu yoktur,asenkron çalışır.Bu yüzden sık-sık tercih edilir.
Sık kulladığımız mvc yöntemi gibi; Component modelden bilgiyi alır, işler ve Component adındaki View'e gönderir.
Envoke metodunu tetiklemek yeterlidir.Yerleşeceği View'in modeline, bağımlılığı yoktur.
3 çeşit yazabiliriz.
1-ViewComponent olduğunu belirtmek için adının sonuna ..ViewComponent diyebiliriz
2- ...:ViewComponent - ViewComponent ten türediğini belirtebiliriz.
3- [ViewComponent] metodun başına annotations ekleyebiliriz.

Şimdi bir Component sınıf oluşturalım ve KategoriLitesi barındıralım
Bir klasör oluşturalım ve Component yazalım. Adı Component, ViewComponent ya da ViewsComponent olabilir.
ViewComponentler, bir IViewComponentResult (ya da ViewComponent)türünden dönerler.
Bu metotlar asenkron çalışırlar. Invoke adında bir metot ile çalışırlar.

    [ViewComponent]
    public class CategoryList:ViewComponent
    {
        IUnitOfWork uow;
        public CategoryList(IUnitOfWork _prm)
        {
            uow = _prm;
        }
        public IViewComponentResult Invoke() //Invoke çağırmak demektir.
        {
            var categorylist = uow.Category.GetAll().ToList();
            return View(categorylist);
        }
    }

Bu metodu gösterecek birde view görünüm oluşturmamız gerekir.
Views klasörü - Shared klasörü içerisinde Components adında bir klasör oluşturalım.Orda da Componentin adında CategoryList bir klasör oluşturalım ve Default.cshtml oluşturalım.

@model ICollection<Category>

<a href="#">Categories</a>
<ul>
    @foreach (var item in Model)
    {
        <li>@item.Name</li>
    }
</ul>

Şimdi istediğimiz alana, modelinden bağımsız olarak , componenti çağrmak.


@await Component.InvokeAsync("CategoryList")
Not:Category ve Alt Category yi daha da şekillendirmek istersek
 
        <ul >

        @foreach (var item in Model.Where(x=> x.ParentID==null))
        {
        <li><a href="#"><i class="fa fa-th-list" aria-hidden="true"></i>@item.Name</a>
        <ul>
             @{ var childcategories = Model.Where(x => x.ParentID == item.ID).ToList();}         
            @for(int i=0; i< childcategories.Count; i++)
            {
                if (i%3 ==1)
                {
                     <li><a href="#"><i class="fa  fa-arrow-right" aria-hidden="true"></i>@childcategories[i].Name</a></li>
                }
                else if (i%3 ==2)
                {
                   <li><a href="#"><i class="fa  fa-arrow-right" aria-hidden="true"></i>@childcategories[i].Name</a></li>
                }
                else if (i%3 ==0)
                {
                   <li><a href="#"><i class="fa  fa-arrow-right" aria-hidden="true"></i>@childcategories[i].Name</a></li>
                }

            }
        </ul>
        </li> 
        }       
        </ul>



İlk önce kayıt yapalım. Register işlemi, kullanıcı adı ,email ve şifre gerekicek.
Şifreleri veritabanında hash lemek için bir metot yazarız. SHA256 kullanabiliriz. Bunun için bir helper yaratırız
Bigiler post edildiği sırada Validation sağlıyor mu diye kontrol edeceğiz.
Örneğin şifresi 5-20 arası karakter olmadığında bir hata fırlatsın isteyeceğiz.

    public class RegisterVM
    {
        [Required]
        public string UserName { get; set; }
        [Required]
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }

        [Required]
        [StringLength(20,MinimumLength =5, ErrorMessage ="Password must be 5-20 chracters.")]
        public string Password { get; set; }
        [Compare("Password",ErrorMessage ="Passwords dont match")]
        public string PasswordConfirm { get; set; }
    }

Ef Core da UserName propertisine Unique kısıtlayıcısı koymak istersek,

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>().HasIndex(u => u.UserName).IsUnique();
        }

        IUnitOfWork uow;
        public UserController(IUnitOfWork prm)
        {
            uow = prm;
        }
        public IActionResult Register()
        {
            return View();
        }
        [HttpPost]
        public IActionResult Register(RegisterVM prm) //parametre olarak model gelicek. model de yazabiliriz.
        {
        
            if (ModelState.IsValid)
            {
                //DB mmodelimizle eşleştirip ekleyeceğiz
                uow.User.Add(new User
                {
                    InsertDate = DateTime.Now,
                    IsActive = true,
                    IsDeleted = false,
                    Email = prm.Email,
                    Password = prm.Password,
                    UserName = prm.UserName
                });
                //SaveChanges metod olarak değilde bir property olrk oluşturduk.
                int result = uow.Save;
                TempData["Message"] = result > 0 ? "oldu" : "olmadı";
            }
            return View();
        }

<div class="row">
    <div class="col-md-4">
        @if ( TempData["Message"] != null)
        {
            <div class="alert alert-name"> @TempData["Message"]</div>
        }

        <form asp-action="Register"  method="post" asp-controller="User">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="UserName" class="control-label"></label>
                <input asp-for="UserName" class="form-control" />
                <span asp-validation-for="UserName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Email" class="control-label"></label>
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Password" class="control-label"></label>
                <input asp-for="Password" class="form-control" />
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="PasswordConfirm" class="control-label"></label>
                <input asp-for="PasswordConfirm" class="form-control" />
                <span asp-validation-for="PasswordConfirm" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

Soru: Unique bir kısıtlayıcı(constraint) mı yoksa dataanotations mı

LoginViewModel oluşturacağız.
ilk öce REpository de Login metot tanımlayacağız.

Login Metodu tanımlarken, Metodu Repository katmanında tanımlayacağız ve Bll katmanında getireceğiz.Öyleyse Dal Repository Katmanında;


namespace Develope.Dal.Abstract
{
  public  interface IUserRepository : IRepository
    {
        User Login(string username, string password);
    }

}


namespace Develope.Dal.Concrete
{
   public class UserRepository : EFRepository, IUserRepository
    {
        public UserRepository(DeveloperContext prm) : base(prm)
        {

        }

        public User Login(string username, string password)
        {
            return Get(x => x.UserName == username && x.Password == password).Data;
        }
    }
}



Şimdi Bll katmanına girelim ve metodları çağırlım.


namespace Developer.Bll.Abstract
{
   public interface IUserService :IDisposable
    { 
        User Login(string username, string password);
    }
}
namespace Developer.Bll.Concrete
{
  public  class UserService :IUserService
    {
        IUserRepository _userRepository;
        public UserService(IUserRepository prm)
        {
            _userRepository = prm;
        }

        public User Login(string username, string password)
        {
            return _userRepository.Login(username,password);
        }
    }

İlk modelimizi oluşturalım.


   public class LoginVM
    {
        [Required]
        public string UserName { get; set; } 

        [Required]
        [StringLength(20, MinimumLength = 5, ErrorMessage = "Password must be 5-20 chracters.")]
        public string Password { get; set; }
    }

Session kullanaağız.
Sessionın sırası önemlidir önce session sonra mvc route olmalı örneğin
Startup.cs de kullaacağımızı belirtelim.


    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSession(x=>
            {
                x.IdleTimeout = TimeSpan.FromSeconds(10);
                x.Cookie.HttpOnly = true;
                x.Cookie.IsEssential = true;
            });
            services.AddHttpContextAccessor();  

            services.AddMvc(x => x.EnableEndpointRouting = false);
        ... ...

       public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
           app.UseSession();
            app.UseMvc(x =>
           {

               x.MapRoute( "areas", "{area:exists}/{controller}/{action}/{id?}", new {controller="Home",Action="Index" });//varsa çatışmasın
               x.MapRoute("default", "{controller}/{action}/{id?}", new { controller = "Home", Action = "Index" }); 
                
           });  

....

UserController da Login metodumuzu tanımlayalm.

  public class UserController : Controller
    {
        //Temsili nesne yaratalım
        IUnitOfWork uow;
        private readonly IHttpContextAccessor httpContextAccessor;
        public UserController(IUnitOfWork prm, IHttpContextAccessor _accessor)
        {
            uow = prm;
            httpContextAccessor = _accessor;
        }

        public IActionResult Login()
        {
            return View();
        }
        [HttpPost]
        public IActionResult Login(LoginVM prm)
        {
            if (ModelState.IsValid)
            {
             User girdi =  uow.User.Login(prm.UserName, prm.Password); //kullanıcı bilgiyi girer. 
            // Kullanıcınn bilgilerini, tüm sayfalarda erişebilen session nesnesine basarız.
                if (girdi !=null)
                {
                    httpContextAccessor.HttpContext.Session.SetInt32("userId", girdi.ID);
                    int userid = httpContextAccessor.HttpContext.Session.GetInt32("userId").Value;

                    httpContextAccessor.HttpContext.Session.SetString("username", girdi.UserName);
 
                } 
            }
            return View();
        }







Hadi şimdi bir component oluşturup login lgout unu ekleyelim.Layout sayfamıza

       @await  Component.InvokeAsync("LoginLogout")

Şimdide Component sınıfını oluşturalım.


    [ViewComponent]
    public class LoginLogout:ViewComponent
    {
        IHttpContextAccessor accessor;
        public LoginLogout(IHttpContextAccessor _accessor)
        {
            accessor = _accessor;
        }

        public IViewComponentResult Invoke()
        {
          string username=  accessor.HttpContext.Session.GetString("username");
            return View((object)username);
        }
    }

Component klasörümüzde Loginlogout adında klasör oluşturup Default sayfamızı ekleyelim.

@model  string
@if (String.IsNullOrEmpty(Model)) ya da
@if (Model ==null)
{
    <ul>
        <li><a href="/User/Register">Signup</a></li>
        <li><a href="/User/Login">Login</a></li>
    </ul>
}
else
{
<ul>
    <li><a href="/User/logout">@Model, Logout için</a></li>
</ul>
}

Controllerda logout metodumuzu belirleyelim.


        public IActionResult LogOut()
        {
            httpContextAccessor.HttpContext.Session.Remove("username");
            httpContextAccessor.HttpContext.Session.Remove("userId");
            return RedirectToAction("Index", "Home");
        }




Session Extensions

Custom session extension oluşturalım. İsteidğiniz bir klasörde SessionExtension adında sınıf yaratalım.


    public static class SessionExtension
    {
        public static void SetObject(this ISession session, string key, object data)
        {
            string value = JsonConvert.SerializeObject(data);
            session.SetString(key,value);
        }

        public static T GetObject<T>(this ISession session, string key)
        {
            string data = session.GetString(key);
            return String.IsNullOrEmpty(data) ? default(T) : JsonConvert.DeserializeObject<T>(data);
        }
    }

Controllerda using sessionextension sınıfımızı tanımlayaıp ekleme yapalım.


        public IActionResult Login()
        {
            return View();
        }
        [HttpPost]
        public IActionResult Login(LoginVM prm)
        {
            if (ModelState.IsValid)
            {
             User girdi =  uow.User.Login(prm.UserName, prm.Password); //kullanıcı bilgiyi girer. 
            // Kullanıcınn bilgilerini, tüm sayfalarda erişebilen session nesnesine basarız.
                if (girdi !=null)
                {
                    httpContextAccessor.HttpContext.Session.SetInt32("userId", girdi.ID);
                    int userid = httpContextAccessor.HttpContext.Session.GetInt32("userId").Value;

                    httpContextAccessor.HttpContext.Session.SetString("username", girdi.UserName);


                    //using Developer.Ui.Models.SessionExtension;
                    httpContextAccessor.HttpContext.Session.SetObject("user", girdi);
                   var mydata= httpContextAccessor.HttpContext.Session.GetObject<User>("user");
                } 
            }
            return View();
        }



Sadece kendimiz oluşturduğumuz siteyi kullanmak zorunda kamayabiliriz.
Bir e-ticaret oluşturduğumuzda,sanal post için bir bankadan servis almamız gerekecektir.
Dolayısıyla banka da bize sanal post işlemleri yapabilmemiz için metotlara sahip olan hizmet sunmalıdır.
Ya da bir sigorta firmasıyla anlaşma yapıyoruz ve firma bize fiyatlar veriyordur.
Karşılaştırma yapan siteler, en uygun fiyatları bir butonla karşımıza getiriyorlar.
Ya da kendimiz bir servis hizmeti oluşturup dışarıya verebiliriz.
Web API ler, mobilde, java da , .nette kullanılabilir o yüzden ortak bir dilde konuşmamız gerekir.
Bunun için eskiden xml etiketleri vardı. Fakat xml-i biraz zenginleşmiş hali json kullanıyoruz.(javascript object Notation)
Core Apiler varsayılan format olarak json tipinde döner. - MVC ler ise varsayılan olarak xml tipinde döner.

SOAP = simple Object Access Protocol

Kullanışlı ama bazı devantajları vardır. servise bağımlı hale geliyoruz. Projeyi de ynı anda düzenlemek zorunda kalıyoruz.

RESTFULL Servisler

Herhangi bir şekilde programımıza entegre etmemiz gerekmeyecek.
Tek ihtiyacımız olan url ni bilmek ve istek atmak.

Çözüm 'e sağ tıklayalım ve aspNetCore Web uygulaması ekleyelim. Çıkan taslaklardan Apı seçelim.
Web Api bir mvc olmak zorunda değil ama mvc design pattern olduğu için tercih ediliyor.
AppSettings.json a baktığımızda - "AllowedHosts": "*" yazılışını görelim bu Heryerden istekte bulunabilirler demektir.
bunu istediğimiz bir site ile kısıtlaybiliriz.
Controllerda route olarak [Route("[controller]")] ya da [Route("api/[controller]")] yazılabilir.

Metotlarımızı oluşturuyoruz. Bunları kullanırken, mobil uygulamada url kullanılabilir, bir ajax sorgusu yazılıp javascript ile Api ye istekte bulunabiliriz. UR

Url leri (Api) leri test etmek için POSTMAN adında bir siteden yardım alabiliriz.postman indirebiliriz.Postman gelen istekleri test etmemizi sağlar.

Random ve free datalar üreten apiler var. https://randomuser.me/ gibi, https://randomapi.com/
Bize ajax ile istek gönderebilir.

Swagger ile apiyi projemize entegre edebiliriz.Google a talking .net swagger yazarsak daha azla bilgi alabiliriz.
https://www.talkingdotnet.com/add-swagger-to-asp-net-core-2-0-web-api/
Swashbuckle aspnet core paketini Manager konsoladan indirelimBize swagger araçlarını sağlayacaktır.
startup ta, add mvc den sonra swagger ekleyelim.

Startup.cs dosyamızda

using Microsoft.OpenApi.Models;

        public void ConfigureServices(IServiceCollection services)
        { 
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
            });
        }


            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
            });

Çalıştırmadan önce Çözümümüze sağ click ve properties den hata aykılama(Debug)panelinden varsayıan tarayıcı oalrak swagger girelim.
Projenin metodları varsa karşımıza o metotlar gelecek.
Şimdi kendimiz bir metot oluşturalım ve Api ile kaydedelim.
Controller klasörüne UserController oluşturalım.



namespace Developer.Web.Api.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class UserController : ControllerBase
    {
        IUnitOfWork uow;
        public UserController(IUnitOfWork _prm)
        {
            uow = _prm;
        }

        [HttpPost]
        public bool Register(RegisterVM prm)
        {
          uow.User.Add(new Model.User { UserName = prm.UserName, Email = prm.Email, InsertDate = DateTime.Now, IsActive = true, IsDeleted = false, Password = prm.Password, UpdateDate = DateTime.Now });
            int result = uow.Save;
               return result>0 ? true:false  ;
        } 
    }
}

Startup.cs Database i belirtelim

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<DeveloperContext>();  
            services.AddScoped< IUnitOfWork, UnitOfWork>();



        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
 
            using (var scope = app.ApplicationServices.GetRequiredService().CreateScope())
            {
                using (var context = scope.ServiceProvider.GetService())
                {
                    context.Database.Migrate(); 
                }
            }

Tokenlar, bir api de istekte bulunduğumuzda yetkisi olduğum bir Bir sayfada apiye istekte bulunuyoruz.
Biz nasıl oturum açtığımızda kullanıcı bilgilerimiziçin session uyguluyorsak
yada windows uygulamasında kullanıcı bilgilerimizi cookie lere basıyorsak ve bu bunlar üzerinden işlemler yürütebiliyorsam.
başka bir sunucuda başka bir projede istekte bulunuyorum
Dolayısıyla benim giriş yapabilen kişi olduğum, yetkili olduğumu blirtem gerek
Bunun için session göndersem kendi projem içerisinde oluşturduğum nesne gönderemem
Cookie göndersem tarayıı içinde birşey gönderemem.
Dolayısıyla extra bir dataya ihtiyacım oluyor.
Bunun için bi token oluşturuyorum , mükün olduğunca karışık olsun ki herkes ezberden o isteği gönderemesin.
Token oluşturmak için farklı araçlar kullanılıyor, bir eklenti olarak jwt kullaabiliriz.
YAPAMADIK http://www.canertosuner.com/post/asp-net-core-json-web-token-kullanimi

NoSql Standart Sql serverlarda , ilişkisel veritabanları oluşturuyoruz ve örneğin Ef ile işlem yaparken büyük verilerde biraz zaman kaybı olabliyor.
Bunun yerine NoSql adında bir sistem geldi. Veriler artık Daha hızlı ve performansı yüksek şekilde barındırılıyor.
MongoDB,RavenDB , ElasticSearch gibi araçlar nosql in çatısı altındadır.Doküman veritabanı olarak adlandırılırlar
Nosql işlemlerini, gerçekleştirmek içinde Redis adında araç kullanırız.
Bu aracı, sunucularda kurarız ve bizim için cache işlemi yapar.

Redis nedir?

Veriyi belekte tutup, key-value şeklinde tasarlayan açık kaynaklı bir NoSql (Not only Sql)veritabanıdır.
Benzersiz bir Id’ye (key) karşılık bir değer (value) tutar.
Veriyi RAM’de tutuyor olması sunucunun kapanması ile verinin tümden yok olması anlamına geliyor.
Reis ndexleme sunmaz sadece belirli key’e atanmış değeri verir.sorgulama hakkı tanımaz.

Redis Nasıl çalışır?

Redis sunucusu, belirli bir port üzerinden client (istemci) aracılığıyla gelen istekleri dinler.
Client, Redis protocol (TCP) aracılığıyla Redis’le iletişime geçer ve komutları gönderir ve sunucu da bunları işler. Redis hızını bu basitlikten alır. Query veya index yok.
Komutları redis.io/command den bulabiliriz.
Özellikle Kategoriler gibi çok kullanılan, her sayfada gösterilmek istenen büyük değerleri bu şekilde cache de tutabiliriz.

Sesion = server taraflı verilerin tutulduğu nesnelerdir, cahce ise client-side taraflı verilerin tutulduğu nesnelerdir.

Cache işlemi için Redis olmadan, Microsoftun kendi interface ini kullanaraka yapabiliriz. InMemoryCache yapısı. Fakat Redis gibi mimari katmanlar için uygun değil.

Redisi bir konteyner gibi düşüeniliriz. redis.io sitesinden download edebiliriz.
ya da gogle da download redis package for window diyebiliriz (Github da https://github.com/MicrosoftArchive dan indirebiliriz proje indirirsek derleyip çalıştırmak gerek derlediğinizde size 5 adet .exe üretir)
Redisin sunucuda kurulu olması gerekir
Redis server ve redis client ı açalım. Client - default bir port üzerinden çalıştığını görelim
Amacımız kendi projemize dahil etmek.
Projemizin UI startup dosyasına gelelim ve paket olarak ilk önce microsot extension caching redis i yükleyelim.
Startup.cs dosyasına gelelim ve ekleyelim.


        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDistributedMemoryCache();
            services.AddDistributedRedisCache(x=> { x.Configuration = "127.0.0.1:6379"; x.InstanceName = "master";  }); //nesne olarak yazmak için { kullanırız.

Sayfayı ilk defa açtıysam bilgileri veritabanından çek, sonra Redisin cache içerisine at.
KAtegorileri çağırdığımız metoda gidelim. Component yazmıştık.


namespace Developer.Ui.ViewsComponent
{

    [ViewComponent]
    public class CategoryList:ViewComponent
    {
        IDistributedCache distrubuedCache;
        IUnitOfWork uow;
        public CategoryList(IUnitOfWork _prm, IDistributedCache _prm2)
        {
            uow = _prm;
            distrubuedCache = _prm2;
        }
        public IViewComponentResult Invoke()
        {
            string cacheddata = distrubuedCache.GetString("categories");
            if (string.IsNullOrEmpty(cacheddata))
            {
                var list = uow.Category.GetAll().ToList();
                distrubuedCache.SetString("categories", JsonConvert.SerializeObject(list));
                return View(list);
            }
            else
            {
                var cachedcategories = JsonConvert.DeserializeObject<ICollection<Category >> (cacheddata);
                return View(cachedcategories);
            }
        }
            
    }



Neden Find pek tercih edilmez? Çünkü ind PrimaryID ye göre yakalar bunun yerine Where(filter) kullanmamız daha mantıklı.
        public E_Bildiri<T> Get(ExpressionFunc<T, bool>> filter)
        {
               /* var sonuc = ctx.Find<T>(filter); //Find primary Id ye göre yakalar primary key olmasa hata verir */
               var sonuc = ctx.Set<T>().Where(filter).FirstOrDefault();
                return new E_Bildiri<T> { Bildiri = "Getirildi", Data = sonuc, Status = true };
        }

Visual studio da  //TODO: şunu yapacağım.  diye bir kod belirtirsek; bir dahaki programı açtığımızda üst seçeneklerden view TaskView deriz ve n//TODO diye belirttiğimiz tüm listeye görebiliriz. 
        Kod yazdık ama using ya da try catch ya da başka bir mekanizmanın içine yazmayı unuttuysak
        Kodlarımızı seçeriz ctl+K, ctrl+S Kastamonu Sivas , using seç tıkla.

        Abstract oluşturduğunuz birşeyi override etmeden oluşturamazsınız ama virtual oluşturduğunuz birşeyi override etmeden oluşturabilirsiniz base den kalıtım alır.

        Dependency Injection nedir?

        WebConfigi şifrelemek için encryptionlar var onlarlada erişimi gizleyebiliriz ya da Dal katmanına koyup sadece ui ye referans verip projemize gömebiliriz.

        Visual Studio da intellisensiniz çalışmazsa projenizi derleyin.


        Not
        Migrate sırasında hata verirse;
        Microsoft.EntityFrameworkCore.Tools ve entityframework core design eklendiğinden emin olun.
        Bazen entityframework core Design paketi versiyonunu indirmek çözüm olabilir.Örn; 3.00 dan 2.2.6 ya indirmek.
        package.Json, yapılandırma paketinizde yazılıp, başvurularınızda olmayan bir kütüphane olabilir.Dikkat edin
        Çözümünüzdeki (solution), tüm projelerin EF versiyonları nın aynı olmasına dikkat edin.
        Birden çok projeniz varsa bazen diğer projeleri kısa süreliğine projeyi kaldır deyip ana projenizi migrate edebilirsiniz.(Pek önerilmez)
        Migration failed to build derse projenizi tekrar bir derleyin (build).Hatalarınız varsa onları mutlaka çözün.
        Migrations ederken varsayılan projenizin ne olduğuna dikkat edin. Contextiniz hangi projedeyse onu seçin.
        Cannot bind argument to parameter  Path  because it is null. ve ya PowerShell.Utility.dll hatası verirse;
        EntityFramework versiyonunuzu kaldırıp tekrar yükleyin.1.PM> Uninstall-Package EntityFramework -Force
        2.PM> Install-Package EntityFramework -Pre
        AspNet MVC de, base( context_adı) nızı, base(name=context_adı) olarak kısıtlayıcı ekleyip değiştirebilirsiniz.
        örn; base("myDbContext") yerine to base("name=myDbContext")
        EntityType  entity  has no key defined. Define the key for this EntityType. Uyarısı, Entity sınıfınıza [Key] ID yerleştirmeniz gerektiğinin uyarısıdır.
        AspNet Mvc de connection stringinize dikkat edin.Context adınız ve yazılım doğruluğunu tespitleyin.
Hata alındığında Edit proje deyip yüklü kütüphanelerinizi kontrol edin.
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <!--<TargetFramework>netstandard2.0</TargetFramework>-->
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.1">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
 

    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.1">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.1" />
    <!--<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.1" />-->

  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Develope.Core\Develope.Core.csproj" />
    <ProjectReference Include="..\Developer.Model\Developer.Model.csproj" />
    <!--<ProjectReference Include="..\Developer.Ui\Developer.Ui.csproj" />-->
  </ItemGroup>

</Project>

17.02.2020