Asp.NET Core Identity – Kullanıcı ve Rol Modellerinde Custom Property Tanımlamak – IV
Merhaba,
Asp.NET Core Identity yazı dizimizin bu 4. içeriğinde “IdentityUser” sınıfından türeyen “AppUser” isimli kullanıcı modelimize custom property ekleyerek özel kolonlar oluşturmayı inceleyeceğiz ve bunun yanında “IdentityRole” sınıfından türeyen bir kullanıcı rollerini modellediğimiz sınıf tasarlayarak benzer mantıkta o model içerisinde custom propertyler ile nasıl özel kolonlar oluşturulduğunada ekstradan değinmiş olacağız. Ayriyetten önceki içeriklerimiz neticesinde gördüğümüz primary key (Id) yapılanmasının varsayılan olarak nvarchar olarak değil de istediğimiz bir tipte ayarlanmasının (int, string, long vs.) nasıl yapıldığını inceleyeceğiz. O halde buyrun başlayalım…
IdentityUser’dan Türeyen “AppUser” Sınıfına Custom Property Eklemek
Uygulamadaki kullanıcılarımızı modellediğimiz “IdentityUser” sınıfından türetilen “AppUser” sınıfını önceki makalelerde gerçekleştirdiğimiz gibi salt bir şekilde aşağıdaki gibi inşa edersek direkt olarak bir kullanıcıyı temsil eden genel geçer propertylerle veritabanına migrate edilecektir.
public class AppUser : IdentityUser { }
Eğer ki, uygulamamızda kullanıcılarımıza dair daha fazla bilgi tutacaksak ilgili model içerisine aşağıdaki gibi custom propertyleri yerleştirebiliriz.
public class AppUser : IdentityUser { public string Memleket { get; set; } public bool Cinsiyet { get; set; } }
Bu işlem migrate neticesinde oluşturulacak fiziksel tabloya aşağıdaki gibi yansıyacaktır;
Evet… Görüldüğü üzere kullanıcı tablolarında kendimize özel kolonlar tanımlayabilmek bu kadar basit.
Şimdi ise bir rol modeli oluşturmayı inceleyelim.
IdentityRole’den Türeyen “AppRole” Sınıfını Oluşturmak ve Uygulamaya Dahil Etmek
Bir rol modeli tanımlayabilmek için “Models” -> “Authentication” klasörü içerisine “AppRole” isminde sınıf oluşturunuz ve bu sınıfı aşağıdaki gibi IdentityRole sınıfından türetiniz.
public class AppRole : IdentityRole { public DateTime OlusturulmaTarihi { get; set; } }
Artık bu rol modelinin uygulamada kullanılacağını belirtmemiz gerekmektedir. Bunun için DbContext sınıfımıza gelip aşağıdaki gibi çalışma gerçekleştiriyoruz;
public class AppDbContext : IdentityDbContext<AppUser, AppRole, string> { public AppDbContext(DbContextOptions<AppDbContext> dbContext) : base(dbContext) { } }
Yukarıdaki kod bloğunu incelersek eğer, “IdentityDbContext” sınıfına generic olarak user modelinde “AppUser” sınıfının, role modelinde ise “AppRole” sınıfının kullanılacağını belirtmiş oluyoruz. 3. parametrede ise bu yapılanmanın primary key (Id) kolonlarının “string” tipte değerlerle tutulacağını ifade etmiş oluyoruz. Keza makalemizin ileriki satırlarında bu değeri hangi hususlara dayanarak değiştirebiliyoruz kritik yaparak ele alacağız.
Velhasıl bu işlemden sonra “Startup.cs” dosyasındaki “ConfigureServices” metodu içerisinde uygulamaya Identity servisini dahil ettiğimiz “AddIdentity” metodunda oluşturduğumuz “AppRole” sınıfını generic olarak belirtmemiz gerekmektedir.
public void ConfigureServices(IServiceCollection services) { . . services.AddIdentity<AppUser, AppRole>().AddEntityFrameworkStores<AppDbContext>(); . . }
Tüm bu işlemler neticesinde uygulamayı derleyip, tekrar migrationları oluşturduktan sonra update edersek eğer aşağıdaki gibi rol mekanizmasının uygulamaya dahil edildiğini görebileceksiniz…
Bu adımdan da sonra sıra veritabanındaki primary key (Id) kolonları üzerinde manuel tip değişikliğine gelmiştir.
Primary Key(Id) Kolonlarının Tipini Belirleme
Bu işlem için DbContext sınıfımızdaki base class olan “IdentityDbContext” sınıfının sonuncu generic parametresine istediğimiz tipi vermemiz yeterli olacaktır.
Tabi yeterli olacaktır ama gidişatınız bu şekildeyse yukarıdaki olası hatada kaçınılmaz olacaktır. Yapılanmada tüm sistemin tipini string dışında belirleyebilmek istiyorsanız “AppUser” ve “AppRole” sınıflarınında id yapılanmasının tipini generic olarak belirtmeniz gerekmektedir ve bu context sınıfındaki tip ile tutarlı olmalıdır.
“Hoca önceki örneğimizde bu tip string iken bir hata almıyorduk! Neden string dışında yapınca alıyoruz?” sorunuzu duyar gibiyim… Evet, almıyorduk. Çünkü varsayılan tüm yapılanma string olarak tasarlanmıştır. Aksi durumdaki tüm tipleri bu şekilde belirtmemiz gerekmektedir.
Nihai olarak bu noktada migrate edilen veritabanındaki tüm primary key kolonlar belirtilen tipte oluşturulmuş olacaktır.
İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…
Not : Örnek projeyi indirmek için buraya tıklayınız.
Merhabalar Hocam,
Öncelikle ders ve emeğiniz için çok teşekkürler ederim.Örnek olarak StokYeri diye bi entitymiz var ve bu entity’ye bir user değeri verilip(sorumlu kişi) AspNetUsers tablosuna foreignkey olarak atama istiyoruz. Tabloya direkt erişimimiz yok. AppUser sınıfı üzerinden foreignKey yapınca AppUser diye yeni bir tablo oluşturuyor.
Sorum şu ki, Identity’yi sadece login işlemlerinde mi kullanalım.Diğer kullanıcı bilgilerine kolay erişmek için ayrı bir kullanıcı tablosu mu oluşturalım.
public int Id { get; set; }
public string StokYeriAdi { get; set; }
public string UserId { get; set; }//SatisTemsilcisi
public virtual AppUsers User { get; set; }
public string SatisTemsilcisiAdi { get; set; }
Merhaba,
AppUser sınıfına foreign key oluşturduğunuz taktirde ilgili tablo otomatik olarak AppUser entitysinin karşılığı olarak generate edilecek olan AspNetUsers tablosuyla ilişkilendirilmesi gerekmektedir. Muhtemelen gözden kaçan bir hata söz konusudur.
Sorunuza cevap olarak hayır diyebiliriz. Tabi kullanıcının kompleks bilgilerini daha hiyerarşik tutmak ve bir düzen oluşturmak istiyorsanız ilişkisel olarak başka bir tabloda bunu gerçekleştirebilirsiniz lakin tüm bu verileri AppUser entitysi üzerinden ilgili tabloda tutmanında hiçbir sakıncası yoktur. Önerim, optimum seviyede bir kolon söz konusuysa AppUser nesnesi üzerinden generate edilecek olan AspNetUsers tablosunda ilgili verileri tutmanızdır.
Sevgiler.
Hocam merhabalar, ASPNETUSER tablomuzdaki bazı kolonları kaldırmak istiyorum. Mümkün müdür acaba ?
Denemedim lakin şöyle bir fikir verebilirim. İstediğiniz kolonu override edip ardından [NotMapped] ile işaretleyerek deneyiniz. Neticeyi bizimle paylaşırsanız sevinirim.
Kolay gelsin.
Dayanamayıp denedim 🙂
Evet bu şekilde istediğiniz kolonları kaldırabilirsiniz.
Örnek kod;
Migrate ettiğimizde yukarıda belirtilen kolonlar kaldırılmış olacaktır.
Sevgiler.
Cevabı bulunca buraya tekrar bakmak aklıma gelmedi 🙂 Ben şu şekilde buldum:
Zaten aynı işlemi yapıyor ama sizin yönteminiz daha okunaklı ve anlaşılır geldi. Sizinde yorumunuzu alırsam daha anlaşılır olabilir benim açımdan. Bir küçük hatırlatma farkı şöyle var ben context içerisinde yazdım. Migrate yaptığımda farklı bir tablo ismiyle oluşturmuştu. Siz IdentityUser içerisinde yapmışsınız bu işlemi. Sizin yönteminiz aynı şekilde mi sonuçlandı acaba.
Nasıl yani? Migrate neticesinde tabloyu farklı isimde mi oluşturdu? Öyleyse eğer oluşturulan ismi yazar mısınız?
Bu arada geri dönüşünüz ve farklı bir çözüm yolu kazandırdığınız için çok teşekkür ederim.
Rica ederim bizde kendi çapımızda bir şeyler yapmaya çalışıyoruz.
Evet migrate sonrasında tablo ismimiz 5. satırdaki AppUser ismiyle oluşturuldu. Ama bi düzeltme geçeyim ben yanlış kodu atmışım. AppUser yerine IdentityUser olacak orası ve tablo ismini de aşağıdaki şekilde “ToTable” şeklinde veriyoruz. Eğer tablo ismini vermezseniz tablomuzun ismi varsayılan “IdentityUser” olarak gelecektir.
Ama aklımdaki soru şu acaba AspNetUser tablosunu silsem bir sıkıntı olur mu? Çünkü 2 tablo aynı anda olması bize bir avantaj yada dezavantaj sağlar mı? Yada sizin yaptığınız yolu kullanıp varolan tablo üzerinden mi işlem yapmalıyım ? Yani burada nasıl bir yol izlemeliyim onu düşünüyorum.
.ToTable fonksiyonunu kullanmadan veritabanını komple silip tekrar migrate eder misiniz?
Konfigürasyonu tamamladıktan (Update-database) yaptıktan sonra DB tarafında User ve Role’ın ID Kısımlarını int’e elle başarılı bir şekilde çeviriyorum. Fakat Modeller üzerinde…. yaptığım zaman hata alıyorum.
Aynı problemle ben de karşılaştım. Sanırım code first tarafında Primary Key alanlarını değiştirebilmek için tabloaların yeniden oluşturulması gerekiyor. https://github.com/dotnet/efcore/issues/329
selam. anlatim isime baya yaradi. tesekkurler.
Hocam selamlar,
Bu kapsamlı yazı dizisi için öncelikle teşekkürler.
Bir sorum olacaktı. Bir uygulamada rolleri organizasyon bazında vermek istesek nasıl bir yol izleyebiliriz. Örneğin; Admin ve User rolleri olsun. OrganizationID 1 için User rolünü bir kullanıcıya verdik. OrganizationID 2 için User rolünü başka bir kullanıcıya verdik. Bu kullanıcılar sadece kendi organizasyonu dahilindeki verilere erişip işlem yapabilecek. Böyle bir senaryoyu Identity altyapısı ile nasıl sağlarız?
Merhaba,
Politika Bazlı Kimlik Doğrulamayı incelemenizi öneririm.
İyi çalışmalar…
Hocam merhaba, projemde sizin de anlattığınız gibi kullanıcıların bilgilerini kaydeden bir sınıfım var AppUser diye bu sınıfa kendi propertlerimi ekledim daha sonra Context sınıfında IdentityDbContext e sizin de yaptığınız gibi paremetle olarak verdim ama ben AppUser gibi başka bir tablo daha istiyorum Ama IdentityDbContext ikinci parametre olarak kabul etmiyor yani sisteme öğrenci olarak ve öğretmen olarak ayrı ayrı bilgiler istenip üye olunsun istiyorum böylece veritabanımda da student ve teacher adlı iki tablom olsun istiyorum ama dediğim gibi iki sınıfi beraber kullanamıyorum sadece AppUser kullanılabiliyor. Yardımcı olursanız çok sevinirim.
Merhaba,
Harici bir tabloda veri tutacaksanız onu ayriyetten tasarlamanız ve ef ile manuel kayıt gerçekleştirmeniz gerekmektedir. Identity mekanizmasında IdentityUser türevi tek bir tablo modellenebilmektedir.
Kolay gelsin.
Anladım hocam, yani harici tabloda veri tutacaksam onu yeniden tasarlayacağım peki identity kullanarak tasarladığımız da parola bilgilerini otomatik olarak identity kendisi hashliyordu, kendimiz tasarladığımız da kullanıcıdan email password aldığımız da aldığımız bu bilgilerden password u kendimiz nasıl controller da hashleyip veritabanına kaydedebiliriz? Teşekkür ederim.
Buradan ya da buradan konuya dair bilgi edinebilirsiniz.
Çok teşekkür ederim hocam.
Hocam dediğiniz gibi yaptım, identity kullandım başka tablo içinde ayrı şekilde yaptım tasarımını yani identity için bir identitydbcontext oluşturdum diğer tablo içinde normal dbcontext oluşturdum ama migration yaparken hata verdi iki farklı dbcontext kullanıyorsunuz diye,bunun önüne nasıl geçebilirim?Teşekkür ederim.
Peki iki farklı context ile mi yaptın?
Eğer öyleyse sadece IdentityDbContext’te yapman yeterli olacaktır.
Hocam bu sefer sadece IdentityDbContext kullandım ikisi içinde ama yine aynı hatayı aldım, AppDbContext, IdentityDbContext den miras alıyordu bu IdentityDbContext de tek parametre kabul ettiği için ikinci tablomu parametre olarak veremiyordum iki tabloyu tek sınıfta birleştirip birleştirdiğim sınıfı IdentityDbContext e parametre olarak verdim ama yine yansımadı maalesef.
Yaptığın çalışmanın örnek kodlarını paylaşırsan daha net yardımcı olabilirim.
Hocam Merhaba.
User tablosuna TC kolunu ekledim ve müşteri bunun şifrelenmesini istiyor.
Eklediğimiz kolunu password gibi şifreleyen bir yapı var mıdır acaba.
Yardımcı olursanız sevinirim iyi çalışmalar dilerim…
Merhaba,
Şuradaki konuya göz atabilirsiniz.