Hocam ağzınıza sağlık Naçizane: public abstract class PageableQueryRequest { public int Page { get; set; } = 0; public int Size { get; set; } = 5; } public class GetAllProductsQueryRequest : PageableQueryRequest, IRequest { }
Çiçek gibi pattern 🌻 Ayrıca mediatR'da sanırım Handle metodunda geriye bir şey dönmediğimiz vakitlerde kullanabileceğimiz Unit adlı bir struct var hocam, CreateProductCommandResponse dönmek yerine onu dönebilirdik. Belki de ileriki derslerde kullanmışsınızdır ama söylemek istedim yine de. Teşekkür ederim
Yani şu APı olayını ufak tefek bir iki trik dışında anladım sanıyordum. Başa sarıdm resmen. allak bullak oldum :)) Ama anlayacağım 30 kere izlerim gerekirse :)
2 ปีที่แล้ว +4
Anlaşılmayacak bişey yok :) Konfigürasyonu yap, controller'ı tasarla, ayağa kaldır, http'den istekleri karşıla ve sonucu döndür :)
Meraba hocam. Sizin derslerinizi cok buyuk zevkle izliyorum. Tesekkur ederim. Bir sorum vardi. JWT Token ve security istifade etdiyimizde JWT deki user ID-sini nerede cikarmaliyiz? Handler siniflarinda yoksa controllerlerde?
MediaR nuget'inde son sürümde "collection.AddMediatR(typeof(ServiceRegistriation));" kullanımında Could not load type 'MediatR.ServiceFactory' from assembly 'MediatR, Version=12.0.0.0 hatasına çözüm olarak , "collection.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(Assembly.GetExecutingAssembly()));" bir çözüm kullandım. Sorun çıkmadı, iyi çalışmalar
Abdullah hocam haklı ancak bir farkla. Pagination default olarak record ve bu nedenle kalıtım esnasında hata alıyoruz. Ancak Pagination'u class yaptık mı bu hata da gidiyor. GetAllProductQueryRequest'in son hali aşağıdaki gibi sade oluyor: public class GetAllProductQueryRequest : Pagination, IRequest { }
2 ปีที่แล้ว +1
@@ataliz01 Evet, biz Pagination'ı record olarak tasarlamıştık. Haliyle record'lar class'larla kalıtımsal ilişkiye giremez. Ya class yapılmalı ya da farklı bir davranış ortaya konmalı.
Hocam cqrs te handlerlar da hep bir objenin veritabanına eklenmesi çıkarılması işlemi yapılıyor. Business işleri handler da yapılmıyor. Biz normalde n katlı mimaride business katmanımız oluyordu, o servis sınıflarında birçok sorgulama, farklı servislere http call yapıp onun sonucuna göre başka işlemler yapma gibi bir sürü birbirinden farklı işlem yapıyorduk. Bu mimaride bu business işlerini nasıl yapacağız?
Hocam, elinize, emeğinize yüreğinize sağlık. Çok değerli bir çalışma olmuş. Bir request hem body, hem queryString, hem de Headers içerisinden parametre alıyorsa, bu konuda nasıl bir yol izleyeceğimizi kısaca anlatabilir misiniz?
Hocam controller tarafındaki endpointi ( [FromQuery] Pagination pagination) olarak set edersek, GetAllProductsQuery classının constructoru ile queryde gelen paginationu, class içindeki paginationa set edebiliriz. Ben bu şekilde yapıyorum. Yani şu şekilde _mediator.Send(new GetAllProductsQuery(pagination));
@ hocam hatta ve hatta global bir pagination actionfilterler ile set edilebilir. Böylelikle her endpointe parametre olarak paginationu yerleştirmek zorunda kalmayız. Yani action filter ile gelen istek incelenir, global tek bir pagination set edilir, logic tarafında da bu pagination kontrol edilebilir. Yani bu sayede bütün isteklerimize otomatik olarak pagination yeteneği kazandırmış oluruz.
@@MuratFirtina hocam şu şekilde yaptım. Bir RequestInfoService servisi oluşturdum. İçeriye httpcontext'i DI ile aldım. Bu servise bir de GetPagination() metodu oluşturdum. Dolayısıyla RequestInfoService classı üzerinden istediğim an istediğim yerde paginationu alabiliyorum. Böylelikle hem action filter kullanmamış (gereksiz yere her requesti action filter ile kesmemiş) oldum, hem de daha verimli bir yapı kurmuş oldum. Umarım anlatabilmişimdir biraz karışık oldu 😂
Video için teşekkürler. Bir ürün getirme işlemi için sırasıyla şu arayüzleri kullanıyoruz. IMediatr -> IService -> IRepository -> DbContext. Bu tarz referansların referansları çağırdığı uygulamalarda ortaya çıkacak performans kaybı göz ardı edilebilecek düzeyde midir? Küçük ölçekli uygulamalarda sizce bu tarz yoğun bir soyutlamaya ihtiyaç var mıdır ?
Gencay Hocam, kafama takilan bir soru var. Ve yanitini uzun zamandir bulamiyorum. Onion Arc da ve CQRS pattern üzerinde Unit Of Work patternini kullanmaya gerek var mi? Varsa nasil ve yapimizin hangi bölümünde yer vermeliyiz. Artisi ve eksisi ne olur. ❓❓❓ Client tarafinda bir cok post islemi gerceklestiginde islemlerin herhangi bir noktada patlamasi sonucu DB de olusacak veri kazalarina karsi UnitOfWork kullaniyorduk. CQRS islemler toplu olarak mi yapiliyor yoksa tek tek mi? Sonuc itibari ile UnitOfWork bizi rollback olaylarindan koruyordu.
Hocam emeğinize sağlık fakat ben birşey sormak istiyorum. Şimdi CQRS dediğimiz pattern komut ve sorguları ayrılmasını söyleyen bi yapı. Fakat bu mantığı persistance tarafında servis sınıflarında crud işlemlerini koyarak tekrar ezmiş olmuyormusunuz. Applciation tarafında handler yapısında direk business logici yapsanız ve repository enjekte ederek o şekilde bir ekleme yapsanız daha iyi olmazmı. hem komut ve sorguları ayırmak için application tarafında CQRS yapısı eklediniz fakat persistance tarafındaki somut nesnelere CRUD işlemlerini ekleyerek komut ve sorguları yine birleştirdiniz. application tarafında direk persistance deki repositroyleri kullansanız daha iyi olmazmıydı ? Artniyetimden değil sadece aklıma takıldığı için neden servis yapılarına ihtiyaç duyduğumuzu anlamak için soruyorum :)
2 ปีที่แล้ว +2
Öncelikle düşünce katkınız için teşekkür ederim. Ne artniyeti canım? Yani bu diyarda kimler bey olduysa artık, 'neden' sorusuna karşı verdikleri tepki insanlarımız tarafından genellenmiş ve artniyet olarak yorumlanabilecek şekilde çekinilecek bir kalıp haline gelmiş. Gayet güzel bir şekilde düşüncenizi paylaşmışsınız ve bu kanalda bunlara bizler hep açığız ve hatta benim öncelikli arzu ettiğim ideal ortam budur desem yeridir. Sorunuza gelirsek eğer, evet handler sınıflarında da operasyonları gerçekleştirebilirdik ama buradaki ihtiyaçların değişebilirliğini göz önüne alarak, bu değişkenliği Application katmanından soyutlamak istedim ve tüm somut operasyonları persistence katmanında gerçekleştirdim. Haliyle gün gelir davranışlarımız değişirse Core'da değil daha üst katmanlarda çalışma yapılması gerekecek ve bu da onion arc. temel hedeflerinden birisidir diyebiliriz. Ayriyeten command ve query'lerin handle edildikleri noktaları ayırmak demek bu operasyonları aynı servislerden besleyemeyiz anlamına gelmemektedir. Aslında buradaki amacın Event Sourcing'e uygun bir temel atmış olduğunu tecrübe etseniz ne demek istediğimi daha net anlayacaksınız :) Teşekkür ederim. Sevgilerimle.
@ Anladım hocam. tam anlamıyla kavramak için servis yapılarına ihtiyacın olduğu bir durumla karşılaşmam gerekiyo. onuda sizin videolarınızla halledeceğim. Teşekkür ederim cevabınız için :)
Merhabalar, viewmodel üzerinde Fluent Validation uygulayıp kullanıyorduk artık request üzerinden istekleri karşılıyoruz validation nasıl çalışacak onu anlayamadım. Validatona CQRS request sınıfını verdim. Bir noktayı atlayıp atlamadığıma emin olmak için soruyorum :D.
Selamlar, öncelikle emekleriniz için teşekkürler dilerim. Küçük bir sorum olacaktı mediatr ait olan bu handels sınıflarımızı IoC sayesinde somut sınıflarımızı applicationda bulundurabiliyoruz fakat düzenli ve daha mantıklı durması açısından neden infrastructure katmanında gerçekleştirmiyoruz sonuçta dış bir kaynağa bağlanıyoruz ve ikinci bir sorum olarak varsaylım ki mediatr kullanmıyoruz ve repositorylerimizi kullanıdğımız servislerimiz var bu servislerimizin soyut ve somut sınıflarını da applicationda mı bulundurmamız gerek yoksa infrastructure mi yoksa bu iki konu isteğe bağlı yazılımcının keyfine kalmış bir şey mi sonuçta servislerimiz de olsa mediatr olsa onları besleyecek olan respositoryler bize IoC aracılığı ile iletileceği için mantık olarak iki katmanda da barınabilir fakat en doğrusu en makulu nedir bilmek istedim . (Benim düşüncem IoC sayesinde katman bağımlığımız olmasa da infrastructure katmanında olması daha doğru gibi geliyor.)
2 ปีที่แล้ว +2
1. Esasında handler sınıfları infrastructure'da dış kaynağı tüketen servisleri çağırarak iş yapmalıdır. Bizler 40. derslere doğru bazı çalışmaları üşengeçlikten dolayı direkt handler'da yapsakta daha ilerideki derslerde bu duruma el atıp olması gereken doğru yaklaşımı sergiliyor olacağız. Ama dediğim gibi doğrusu handler sınıflarının tüm business operasyonları inf. katmanındaki sınıflar aracılığıyla IoC üzerinden erişerek yürütmesidir. 2. Concrete repository ya da servis sınıfları kesinlikle inf. katmanında olmalıdır. Veritabanı ile ilgili ise(repository) persistence tabi. Çünkü application davranıştan da bağımsız olmalıdır! Not : Bizler uygulama sürecinde application katmanını identity vs. gibi bazı davranışlara yukarıdaki satırlarda dediğim gibi üşengeçlikten bağımlı kılıyoruz. Doğru değil! İçim elvermiyor o yüzden ilerideki derslerde gerekli düzeltmeleri yapacağız 🥸😂
@ Yanıtınız için de teşekkür ederim yani aslında doğru anlamışım handler sınıflarmızda inf katmanında olması mantık olarak daha doğru aynı şeklide ileride servislerimiz de olursa onlarında inf katmanında olması gerek. Herşey için çok teşekkürler :)
Burda ben bir yeri tam anlamadim hocam. Business logic/domain logic domain entities icerisinde olmasi gerekmiyor mu? Ornek Order class icerisinde AddOrderItem isimli bir method ile orderItem property`nin setini private yapip disaridan erisem kapatip (encapsulate) logic`i bu method icerisinde uygulamamiz daha dogru degil mi? Boylece ornegin odemesi gerekmesmemis bir urunu gondermenin onude gecmemiyiz validation uygularak??
Hocam mediator kütüphanesini indirmesek olmaz mı? Ne bileyim yarın öbür gün kütüphanede oluşacak bir bug veya geliştirilmesinin durdurulması durumunda ortada kalmayalım. Ben de hep bu korku olduğu için kullanasım gelmiyor.
Hocam public Pagination Pagination => Pagination pagination böylemi olması lazım ikiside büyük harf olunca mı karışıyor) ??
2 ปีที่แล้ว +2
Yok o önemli değil. Sembolik olarak şöyle izah edeyim; "A" türünden bir property tanımlıyorsak eğer bu property'nin adı yine "A" olabilir. public A A {get; set;] -> Compiler burada hangisinin tür hangisinin yapı ismi olduğunu ayırt edebiliyor.
Hocam apideki her bir action için ayrı ayrı klasörler oluşturduk, klasörlerde de ayrı 3 class var okunurluk açısından zor ve dosya kalabalığı oluyor. Her bir action için örneğin GetAllProducts, Queries altına bir GetAllProducts class'ı oluştursak, içerisine de Request,Handler,Response class'larımızı nested class olarak koysak aykırı bir durum yaratır mı?
2 ปีที่แล้ว +2
Hayır :) İstediğiniz gibi yapabilirsiniz. Ben eğitim sürecinde bu şekilde göstere göstere yapmayı tercih ediyorum.
MediatR.Extensions.Microsoft.DependencyInjection kütüphanesinin deprecate olduğunu öğrendim. Alternatif bir kütüphane mi yüklemeliyim yoksa MediatR kütüphanesi artık bunu da destekliyor mu?
hocam hata ayıklama için response class'ına state ve error ekledim dictionary yapısı ile, controller'da state göre return işlemi yapıyor acaba yapıya aykırı bir durum varmı ?
Hocam her şey iyi hoş. Ben devasa api'mi generic repostiroy pattern ile yapmıştım. Jwt dahil her şey de çalışıyor. Sizin videoları izleyerek dönüştürüyorum. Fakat benim api'm de global mesaj yönetimi var. Mesela bir kategori eklediğimde bana response olarak "... isimli kategori başarıyla eklendi" diye mesaj dönüyor. Veya tüm ürünleri listelediğimde "Tüm ürünler başarıyla listelendi mesajını dönüyor". Tabi ürünleri de listeliyor. Şimdi ben bu mesaj sınıfımı static tanımladım. Ve çok da güzel çalışıyor. Cqrs'de ben bunu nereye koyacağım nasıl yapacağım pek emin olamadım. Sanırım inf katmanında koymam lazım. Zira veritabanına etkisi yok. Mesajları da response sınıflarına mı yazmam lazım? 1 tane örnek yapar mısınız? Kalanını hallederim 1 örnek görürsem.
@ Hocam Messages classım aşağıdaki gibi. Bu metodu mesaj göstermek istediğim yerde çağırıyorum ve parametreleri gönderiyorum. Switch case ile gelen entitye göre dinamik bir şekilde mesajı dönüyor. En altta örnek kullanım da koydum. Bunun için ResultStatus, DataResult vs adında sınıflarım var. Yapılan işlem gerçekleşince bu sınıf ile mesajı alıyorum. public class Messages { public static string Info(string entity, string method, string value = null, string currentTitle = null) { string result = ""; switch (entity) { case "User": entity = "kullanıcı"; break; case "Kultur": entity = "kültür"; break; case "Article": entity = "makale"; break; case "ArticleCategory": entity = "kategori"; break; case "CardCategory": entity = "kategori"; break; case "Card": entity = "kart"; break; } switch (method) { case "Get": result = $"{value} isimli {entity} başarıyla getirildi."; break; case "ValidUser": result = $"Sayın {value}, başarıyla giriş yaptınız."; break; case "RegisterUser": result = $"Sayın {value}, üyelik işleminizi tamamlamak için email adresinize gönderdiğimiz linke tıklayıp üyeliğinizi onaylayınız."; break; case "LogoutUser": result = $"Başarıyla çıkış yaptınız."; break; case "NotLogoutUser": result = $"Çıkış yaparken bir hata meydana geldi."; break; case "ErrorUser": result = $"E-posta adresinizi veya şifrenizi hatalı girdiniz."; break; case "NotAdded": result = $"{value} isimli {entity} eklenemedi."; break; case "NotFoundSingle": result = $"Böyle bir {entity} bulunamadı."; break; case "NotFoundPlural": result = $"Hiçbir {entity} bulunamadı."; break; case "GetAll": result = $"Tüm {entity} öğeleri başarıyla getirildi."; break; case "GetAllByCategory": result = $"Seçilen kategoriye göre tüm {entity} öğeleri başarıyla getirildi."; break; case "GetAllDeleted": result = $"Tüm silinmiş {entity} öğeleri başarıyla getirildi."; break; case "GetAllNonDeleted": result = $"Tüm silinmemiş {entity} öğeleri başarıyla getirildi."; break; case "Add": result = $"{value} başarıyla eklendi."; break; case "InUse": if (entity == "kullanıcı") { result = $"{value} isminde bir kullanıcı adı zaten bulunuyor. Lütfen farklı bir isim giriniz."; } break; case "InUseEmail": result = $"{value} e-posta adresi ile daha önce kayıt olunmuş. Lütfen farklı bir e-posta adresi giriniz veya şifrenizi sıfırlayınız."; break; case "Update": if (value != currentTitle) { return $"{currentTitle}, {value} olarak değiştirilip başarıyla güncellendi."; } result = $"{value} başarıyla güncellendi."; break; case "Delete": result = $"{value} başarıyla silinenlere taşındı."; break; case "HardDelete": result = $"{value} tamamen silindi."; break; case "HardDeleteRange": result = $"Seçilen {entity} kayıtları silindi."; break; case "GetBack": result = $"{value} başarıyla geri getirildi."; break; case "Count": result = $"{value} sayısı belirlenemedi."; break; case "Draft": result = $"Tüm taslak olan {entity} öğeleri başarıyla getirildi."; break; } return result; } } // Aşağıda tek kullanıcıyı çekerken bir DataResult dönüyorum. Kullanıcı bulunursa ResultStatus.Succes ile beraber Mesajı dönüyorum. Bulamazsam ResultStatus.Error dönüyorum. public async Task GetAsync(Guid id) { var wGetted = await _memberManager.FindByIdAsync(id.ToString()); if (wGetted != null) { return new DataResult(ResultStatus.Success, new UserDto { User = wGetted, Message = Messages.Info("User", "Get", wGetted.UserName) }); } return new DataResult(ResultStatus.Error, new UserDto { User = null, Message = Messages.Info("User", "NotFoundSingle") }); } //DataResult ile data içeren result dönüyorum. Bir de sadece result var. Onda da data içermeyenleri dönüyorum. Mesela kullanıcı silindi vs. public async Task HardDeleteAsync(Guid id) { var user = await _memberManager.FindByIdAsync(id.ToString()); if (user != null) { if (user.IsDeleted == false) { user.IsDeleted = true; } var hardDeleted = await _memberManager.DeleteAsync(user); if (hardDeleted.Succeeded) { return new Result(ResultStatus.Success, Messages.Info("User", "HardDelete", user.UserName)); } } return new Result(ResultStatus.Error, Messages.Info("User", "NotFoundSingle")); }
@@kagancosar7470 Çözdüm. Çok da güzel çalışıyor. //Response içerisine geriye mesaj dönmek istediğim için onu ekledim. Bir de ne oluşturduysam onu geri dönmek istedim. public class CreateCategoryCommandResponse { public virtual string Message { get; set; } public Category Category { get; set; } } //Handlerım da aşağıdaki gibi. En altta Response metodu dönüyorum. İçine de eklenen kategori nesnesini ve mesajımı yolluyorum. public async Task Handle(CreateCategoryCommandRequest request, CancellationToken cancellationToken) { if (request.Name != null && request.Name.Length > 0) { var category = await _categoryCrudRepository.AddAsync(new() { Name = request.Name.ToLower().Trim(), Ikon = request.Ikon.Trim(), Description = request.Description.Trim() }); var result = await _categoryCrudRepository.SaveAsync(); if (result == 1) { return new CreateCategoryCommandResponse() { Category = category, Message = Messages.Info("Category", "Add", category.Name) }; } else { return new CreateCategoryCommandResponse() { Category = null, Message = Messages.Info("Category", "NotAdded", request.Name) }; } } return new CreateCategoryCommandResponse() { Category = null, Message = Messages.Info("Category", "Empty") }; } //Messages sınıfım aşağıdaki gibi. Dönecek mesaj şöyle "category.Name adlı kategori başarıyla eklendi". Switch case yapısı kurdum ki dinamik olsun. Tek bir sınıfla tüm mesajları globalleştirmiş oldum böylece. Aşağıda sadece add metodu için yazdım. Diğer metotlar da var ama ben burada kısaca yazdım. public static string Info(string entity, string method, string value = null, string currentName = null) { string result = ""; switch (entity) { case "Category": entity = "kategori"; break; } switch (method) {
case "NotAdded": result = $"{value} isimli {entity} eklenemedi."; break; case "Empty": result = $"Zorunlu alanlar boş bırakılamaz."; break; case "Add": result = $"{value} isimli {entity} başarıyla eklendi."; break; } return result; }
Merhaba hocam, Application katmanında ServiceRegistration içinde services.AddMediatR(typeof(ServiceRegistration)); kullandım anlattığınız gibi. Localde uygulamayı çalıştırdığımda problem olmuyor. Ama bir IIS'e kurduğumda kısaca ("Register your handlers with the container") hatası alıyorum. Handler içinde kullandığım IRepository karşılığında kullanacağı Repository'i çözemiyor olabilir mi ? Nasıl bir yol izlemeliyim ?
Hocam tekrar merhaba, Hatayla ilgili tüm araştırmalarımda service.AddMediatR kısmıyla ilgili çözümlerle karşılaşınca ben de oraya odaklandım. Ama aslında tamamen alakasız bir durummuş. Aslında DbContext'i olması gerektiği gibi oluşturamıyormuş. Çünkü Configration sınıfında yol olarak "../../Presentation/Project.Api" verdik. Burada appsettings.js yi arıyor. Projeyi IIS'e publish olarak attığımda appsettings.json aslında aynı dizinde. 2 üst klasöre çıkıp Presentation klasörünü ve içindeki Poject.Api klasörünü bulamadığı için DbContext ayağa kalkamıyor. Hatayla ilgili gereksiz vaktinizi aldıysam kusura bakmayın..
Hocam, ilk action'ı sizin yaptığınız gibi kodladım ama client tarafında veriler geliyor ama sayfada gözükmüyor. Kodları eski hale getirince sayfada gözüküyor. Hatayı aradım ama bulamadım bu problem neyden kaynaklanıyor olablir?
Tamamdır hocam sorunu buldum değişkenler ile ilgili hata varmış API kısmında ne ise Angular kısmında da aynısını yaparsanız sorun gideriliyor. Çözemediyseniz bilginiz olsun.
aklıma gelen soruları cevapladığınız için teşekkür ederim hocam
şiir gibi ders. Teşekkürler
Film tadında izliyoruz elinize sağlık 🌾
Hocam ağzınıza sağlık
Naçizane:
public abstract class PageableQueryRequest
{
public int Page { get; set; } = 0;
public int Size { get; set; } = 5;
}
public class GetAllProductsQueryRequest : PageableQueryRequest, IRequest
{ }
Naçizane:
işe yaradı :)
18.01.2024 Uygulamalı bitti... inanamıyoruum :) Teşekkürler Hocacımızzz
Hocam, mükemmel anlatmışsınız. Elinize, dilinize ve emeğinize sağlık...
Hocam elinize, emeğinize, yüreğinize sağlık mükemmel anlatıyorsunuz, teşekkürler
kral uzun zamandır seni takip ediyorum ama sindire sindire ilerlemek cok iyi :)
Çiçek gibi pattern 🌻
Ayrıca mediatR'da sanırım Handle metodunda geriye bir şey dönmediğimiz vakitlerde kullanabileceğimiz Unit adlı bir struct var hocam, CreateProductCommandResponse dönmek yerine onu dönebilirdik. Belki de ileriki derslerde kullanmışsınızdır ama söylemek istedim yine de. Teşekkür ederim
Teşekkürler
Elinize sağlık hocam, teşekkürler
Hocam elinize ve emeğinize sağlık mükemmelsiniz
Refleks oldu kızıyorum dediğin an Like tuşuna basıyorum :D Eline sağlık
Dosya karmaşıklığına çözüm ve kolay erişbilirlik için benim kullanıdığım yöntem:
Application
Features
Products
Commands
CreateProductCommand - CreateProductCommandHandler (aynı dosya içerisinde.)
Queries
GetAllProductQuery - GetAllProductQueryHandler (aynı dosya içerisinde.)
Models
CreateProductCommandModel
GetAllProductQueryResponse
Yani şu APı olayını ufak tefek bir iki trik dışında anladım sanıyordum. Başa sarıdm resmen. allak bullak oldum :)) Ama anlayacağım 30 kere izlerim gerekirse :)
Anlaşılmayacak bişey yok :) Konfigürasyonu yap, controller'ı tasarla, ayağa kaldır, http'den istekleri karşıla ve sonucu döndür :)
Meraba hocam. Sizin derslerinizi cok buyuk zevkle izliyorum. Tesekkur ederim. Bir sorum vardi. JWT Token ve security istifade etdiyimizde JWT deki user ID-sini nerede cikarmaliyiz? Handler siniflarinda yoksa controllerlerde?
MediaR nuget'inde son sürümde "collection.AddMediatR(typeof(ServiceRegistriation));" kullanımında Could not load type 'MediatR.ServiceFactory' from assembly 'MediatR, Version=12.0.0.0 hatasına çözüm olarak , "collection.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(Assembly.GetExecutingAssembly()));" bir çözüm kullandım. Sorun çıkmadı, iyi çalışmalar
up
You are "Bir harika"
Pagination.Size gibi istiyor. Yani şu şekilde: `Pagination.Page=${pageNumber}&Pagination.Size=${pageSize}`.
GetAllProductQueryRequest sınıfını Pagination sınıfından kalıtım alırsak propertyleri de gelmiş olacak
Abdullah hocam haklı ancak bir farkla. Pagination default olarak record ve bu nedenle kalıtım esnasında hata alıyoruz. Ancak Pagination'u class yaptık mı bu hata da gidiyor. GetAllProductQueryRequest'in son hali aşağıdaki gibi sade oluyor:
public class GetAllProductQueryRequest : Pagination, IRequest
{
}
@@ataliz01 Evet, biz Pagination'ı record olarak tasarlamıştık. Haliyle record'lar class'larla kalıtımsal ilişkiye giremez. Ya class yapılmalı ya da farklı bir davranış ortaya konmalı.
Hocam cqrs te handlerlar da hep bir objenin veritabanına eklenmesi çıkarılması işlemi yapılıyor. Business işleri handler da yapılmıyor. Biz normalde n katlı mimaride business katmanımız oluyordu, o servis sınıflarında birçok sorgulama, farklı servislere http call yapıp onun sonucuna göre başka işlemler yapma gibi bir sürü birbirinden farklı işlem yapıyorduk. Bu mimaride bu business işlerini nasıl yapacağız?
Hocam, elinize, emeğinize yüreğinize sağlık. Çok değerli bir çalışma olmuş. Bir request hem body, hem queryString, hem de Headers içerisinden parametre alıyorsa, bu konuda nasıl bir yol izleyeceğimizi kısaca anlatabilir misiniz?
Hocam controller tarafındaki endpointi ( [FromQuery] Pagination pagination) olarak set edersek, GetAllProductsQuery classının constructoru ile queryde gelen paginationu, class içindeki paginationa set edebiliriz. Ben bu şekilde yapıyorum. Yani şu şekilde _mediator.Send(new GetAllProductsQuery(pagination));
Olabilir, güzel.
@ hocam hatta ve hatta global bir pagination actionfilterler ile set edilebilir. Böylelikle her endpointe parametre olarak paginationu yerleştirmek zorunda kalmayız. Yani action filter ile gelen istek incelenir, global tek bir pagination set edilir, logic tarafında da bu pagination kontrol edilebilir. Yani bu sayede bütün isteklerimize otomatik olarak pagination yeteneği kazandırmış oluruz.
@@abdulkadirkg bunu modelleyelim
@@abdulkadirkg bunu yaptın mı hocam merak ettim. Varsa kodu paylaşabilir misin?
@@MuratFirtina hocam şu şekilde yaptım. Bir RequestInfoService servisi oluşturdum. İçeriye httpcontext'i DI ile aldım. Bu servise bir de GetPagination() metodu oluşturdum. Dolayısıyla RequestInfoService classı üzerinden istediğim an istediğim yerde paginationu alabiliyorum. Böylelikle hem action filter kullanmamış (gereksiz yere her requesti action filter ile kesmemiş) oldum, hem de daha verimli bir yapı kurmuş oldum. Umarım anlatabilmişimdir biraz karışık oldu 😂
Hocam clean architecture mimarisinde CQRS nasıl kullanılmalı?
Video için teşekkürler. Bir ürün getirme işlemi için sırasıyla şu arayüzleri kullanıyoruz. IMediatr -> IService -> IRepository -> DbContext. Bu tarz referansların referansları çağırdığı uygulamalarda ortaya çıkacak performans kaybı göz ardı edilebilecek düzeyde midir? Küçük ölçekli uygulamalarda sizce bu tarz yoğun bir soyutlamaya ihtiyaç var mıdır ?
Gencay Hocam, kafama takilan bir soru var. Ve yanitini uzun zamandir bulamiyorum. Onion Arc da ve CQRS pattern üzerinde Unit Of Work patternini kullanmaya gerek var mi? Varsa nasil ve yapimizin hangi bölümünde yer vermeliyiz. Artisi ve eksisi ne olur. ❓❓❓ Client tarafinda bir cok post islemi gerceklestiginde islemlerin herhangi bir noktada patlamasi sonucu DB de olusacak veri kazalarina karsi UnitOfWork kullaniyorduk. CQRS islemler toplu olarak mi yapiliyor yoksa tek tek mi? Sonuc itibari ile UnitOfWork bizi rollback olaylarindan koruyordu.
Hocam emeğinize sağlık fakat ben birşey sormak istiyorum. Şimdi CQRS dediğimiz pattern komut ve sorguları ayrılmasını söyleyen bi yapı. Fakat bu mantığı persistance tarafında servis sınıflarında crud işlemlerini koyarak tekrar ezmiş olmuyormusunuz. Applciation tarafında handler yapısında direk business logici yapsanız ve repository enjekte ederek o şekilde bir ekleme yapsanız daha iyi olmazmı. hem komut ve sorguları ayırmak için application tarafında CQRS yapısı eklediniz fakat persistance tarafındaki somut nesnelere CRUD işlemlerini ekleyerek komut ve sorguları yine birleştirdiniz. application tarafında direk persistance deki repositroyleri kullansanız daha iyi olmazmıydı ? Artniyetimden değil sadece aklıma takıldığı için neden servis yapılarına ihtiyaç duyduğumuzu anlamak için soruyorum :)
Öncelikle düşünce katkınız için teşekkür ederim. Ne artniyeti canım? Yani bu diyarda kimler bey olduysa artık, 'neden' sorusuna karşı verdikleri tepki insanlarımız tarafından genellenmiş ve artniyet olarak yorumlanabilecek şekilde çekinilecek bir kalıp haline gelmiş. Gayet güzel bir şekilde düşüncenizi paylaşmışsınız ve bu kanalda bunlara bizler hep açığız ve hatta benim öncelikli arzu ettiğim ideal ortam budur desem yeridir.
Sorunuza gelirsek eğer, evet handler sınıflarında da operasyonları gerçekleştirebilirdik ama buradaki ihtiyaçların değişebilirliğini göz önüne alarak, bu değişkenliği Application katmanından soyutlamak istedim ve tüm somut operasyonları persistence katmanında gerçekleştirdim. Haliyle gün gelir davranışlarımız değişirse Core'da değil daha üst katmanlarda çalışma yapılması gerekecek ve bu da onion arc. temel hedeflerinden birisidir diyebiliriz.
Ayriyeten command ve query'lerin handle edildikleri noktaları ayırmak demek bu operasyonları aynı servislerden besleyemeyiz anlamına gelmemektedir. Aslında buradaki amacın Event Sourcing'e uygun bir temel atmış olduğunu tecrübe etseniz ne demek istediğimi daha net anlayacaksınız :)
Teşekkür ederim.
Sevgilerimle.
@ Anladım hocam. tam anlamıyla kavramak için servis yapılarına ihtiyacın olduğu bir durumla karşılaşmam gerekiyo. onuda sizin videolarınızla halledeceğim. Teşekkür ederim cevabınız için :)
Merhabalar, viewmodel üzerinde Fluent Validation uygulayıp kullanıyorduk artık request üzerinden istekleri karşılıyoruz validation nasıl çalışacak onu anlayamadım. Validatona CQRS request sınıfını verdim. Bir noktayı atlayıp atlamadığıma emin olmak için soruyorum :D.
Selamlar, öncelikle emekleriniz için teşekkürler dilerim. Küçük bir sorum olacaktı mediatr ait olan bu handels sınıflarımızı IoC sayesinde somut sınıflarımızı applicationda bulundurabiliyoruz fakat düzenli ve daha mantıklı durması açısından neden infrastructure katmanında gerçekleştirmiyoruz sonuçta dış bir kaynağa bağlanıyoruz ve ikinci bir sorum olarak varsaylım ki mediatr kullanmıyoruz ve repositorylerimizi kullanıdğımız servislerimiz var bu servislerimizin soyut ve somut sınıflarını da applicationda mı bulundurmamız gerek yoksa infrastructure mi yoksa bu iki konu isteğe bağlı yazılımcının keyfine kalmış bir şey mi sonuçta servislerimiz de olsa mediatr olsa onları besleyecek olan respositoryler bize IoC aracılığı ile iletileceği için mantık olarak iki katmanda da barınabilir fakat en doğrusu en makulu nedir bilmek istedim . (Benim düşüncem IoC sayesinde katman bağımlığımız olmasa da infrastructure katmanında olması daha doğru gibi geliyor.)
1. Esasında handler sınıfları infrastructure'da dış kaynağı tüketen servisleri çağırarak iş yapmalıdır. Bizler 40. derslere doğru bazı çalışmaları üşengeçlikten dolayı direkt handler'da yapsakta daha ilerideki derslerde bu duruma el atıp olması gereken doğru yaklaşımı sergiliyor olacağız. Ama dediğim gibi doğrusu handler sınıflarının tüm business operasyonları inf. katmanındaki sınıflar aracılığıyla IoC üzerinden erişerek yürütmesidir.
2. Concrete repository ya da servis sınıfları kesinlikle inf. katmanında olmalıdır. Veritabanı ile ilgili ise(repository) persistence tabi. Çünkü application davranıştan da bağımsız olmalıdır!
Not : Bizler uygulama sürecinde application katmanını identity vs. gibi bazı davranışlara yukarıdaki satırlarda dediğim gibi üşengeçlikten bağımlı kılıyoruz. Doğru değil! İçim elvermiyor o yüzden ilerideki derslerde gerekli düzeltmeleri yapacağız 🥸😂
@ Yanıtınız için de teşekkür ederim yani aslında doğru anlamışım handler sınıflarmızda inf katmanında olması mantık olarak daha doğru aynı şeklide ileride servislerimiz de olursa onlarında inf katmanında olması gerek. Herşey için çok teşekkürler :)
Burda ben bir yeri tam anlamadim hocam. Business logic/domain logic domain entities icerisinde olmasi gerekmiyor mu? Ornek Order class icerisinde AddOrderItem isimli bir method ile orderItem property`nin setini private yapip disaridan erisem kapatip (encapsulate) logic`i bu method icerisinde uygulamamiz daha dogru degil mi? Boylece ornegin odemesi gerekmesmemis bir urunu gondermenin onude gecmemiyiz validation uygularak??
Hocam parametreleri tek tek yazmak yerine obje olarak gönderme şansımız yok mu? örnek olarak public VM_Create_Product product { get; set; }
Hocam mediator kütüphanesini indirmesek olmaz mı? Ne bileyim yarın öbür gün kütüphanede oluşacak bir bug veya geliştirilmesinin durdurulması durumunda ortada kalmayalım. Ben de hep bu korku olduğu için kullanasım gelmiyor.
O kadar endişeye gerek yok 🙃
Hocam public Pagination Pagination => Pagination pagination böylemi olması lazım ikiside büyük harf olunca mı karışıyor) ??
Yok o önemli değil. Sembolik olarak şöyle izah edeyim;
"A" türünden bir property tanımlıyorsak eğer bu property'nin adı yine "A" olabilir.
public A A {get; set;] -> Compiler burada hangisinin tür hangisinin yapı ismi olduğunu ayırt edebiliyor.
Hocam apideki her bir action için ayrı ayrı klasörler oluşturduk, klasörlerde de ayrı 3 class var okunurluk açısından zor ve dosya kalabalığı oluyor. Her bir action için örneğin GetAllProducts, Queries altına bir GetAllProducts class'ı oluştursak, içerisine de Request,Handler,Response class'larımızı nested class olarak koysak aykırı bir durum yaratır mı?
Hayır :) İstediğiniz gibi yapabilirsiniz. Ben eğitim sürecinde bu şekilde göstere göstere yapmayı tercih ediyorum.
Benim de bu pattern'da gördüğüm tek dezavantaj dosya kalabalığıydı. Sanırım ben de nested olarak yapacağım. Teşekkürler :)
MediatR.Extensions.Microsoft.DependencyInjection kütüphanesinin deprecate olduğunu öğrendim.
Alternatif bir kütüphane mi yüklemeliyim yoksa MediatR kütüphanesi artık bunu da destekliyor mu?
Direkt MediatR ile yola devam.
hocam hata ayıklama için response class'ına state ve error ekledim dictionary yapısı ile, controller'da state göre return işlemi yapıyor acaba yapıya aykırı bir durum varmı ?
Hocam her şey iyi hoş. Ben devasa api'mi generic repostiroy pattern ile yapmıştım. Jwt dahil her şey de çalışıyor. Sizin videoları izleyerek dönüştürüyorum. Fakat benim api'm de global mesaj yönetimi var. Mesela bir kategori eklediğimde bana response olarak "... isimli kategori başarıyla eklendi" diye mesaj dönüyor. Veya tüm ürünleri listelediğimde "Tüm ürünler başarıyla listelendi mesajını dönüyor". Tabi ürünleri de listeliyor. Şimdi ben bu mesaj sınıfımı static tanımladım. Ve çok da güzel çalışıyor. Cqrs'de ben bunu nereye koyacağım nasıl yapacağım pek emin olamadım. Sanırım inf katmanında koymam lazım. Zira veritabanına etkisi yok. Mesajları da response sınıflarına mı yazmam lazım? 1 tane örnek yapar mısınız? Kalanını hallederim 1 örnek görürsem.
Global mesaj yapınızı paylaşır mısınız?
@ Hocam Messages classım aşağıdaki gibi. Bu metodu mesaj göstermek istediğim yerde çağırıyorum ve parametreleri gönderiyorum. Switch case ile gelen entitye göre dinamik bir şekilde mesajı dönüyor. En altta örnek kullanım da koydum. Bunun için ResultStatus, DataResult vs adında sınıflarım var. Yapılan işlem gerçekleşince bu sınıf ile mesajı alıyorum.
public class Messages
{
public static string Info(string entity, string method, string value = null, string currentTitle = null)
{
string result = "";
switch (entity)
{
case "User":
entity = "kullanıcı";
break;
case "Kultur":
entity = "kültür";
break;
case "Article":
entity = "makale";
break;
case "ArticleCategory":
entity = "kategori";
break;
case "CardCategory":
entity = "kategori";
break;
case "Card":
entity = "kart";
break;
}
switch (method)
{
case "Get":
result = $"{value} isimli {entity} başarıyla getirildi.";
break;
case "ValidUser":
result = $"Sayın {value}, başarıyla giriş yaptınız.";
break;
case "RegisterUser":
result = $"Sayın {value}, üyelik işleminizi tamamlamak için email adresinize gönderdiğimiz linke tıklayıp üyeliğinizi onaylayınız.";
break;
case "LogoutUser":
result = $"Başarıyla çıkış yaptınız.";
break;
case "NotLogoutUser":
result = $"Çıkış yaparken bir hata meydana geldi.";
break;
case "ErrorUser":
result = $"E-posta adresinizi veya şifrenizi hatalı girdiniz.";
break;
case "NotAdded":
result = $"{value} isimli {entity} eklenemedi.";
break;
case "NotFoundSingle":
result = $"Böyle bir {entity} bulunamadı.";
break;
case "NotFoundPlural":
result = $"Hiçbir {entity} bulunamadı.";
break;
case "GetAll":
result = $"Tüm {entity} öğeleri başarıyla getirildi.";
break;
case "GetAllByCategory":
result = $"Seçilen kategoriye göre tüm {entity} öğeleri başarıyla getirildi.";
break;
case "GetAllDeleted":
result = $"Tüm silinmiş {entity} öğeleri başarıyla getirildi.";
break;
case "GetAllNonDeleted":
result = $"Tüm silinmemiş {entity} öğeleri başarıyla getirildi.";
break;
case "Add":
result = $"{value} başarıyla eklendi.";
break;
case "InUse":
if (entity == "kullanıcı")
{
result = $"{value} isminde bir kullanıcı adı zaten bulunuyor. Lütfen farklı bir isim giriniz.";
}
break;
case "InUseEmail":
result = $"{value} e-posta adresi ile daha önce kayıt olunmuş. Lütfen farklı bir e-posta adresi giriniz veya şifrenizi sıfırlayınız.";
break;
case "Update":
if (value != currentTitle)
{
return $"{currentTitle}, {value} olarak değiştirilip başarıyla güncellendi.";
}
result = $"{value} başarıyla güncellendi.";
break;
case "Delete":
result = $"{value} başarıyla silinenlere taşındı.";
break;
case "HardDelete":
result = $"{value} tamamen silindi.";
break;
case "HardDeleteRange":
result = $"Seçilen {entity} kayıtları silindi.";
break;
case "GetBack":
result = $"{value} başarıyla geri getirildi.";
break;
case "Count":
result = $"{value} sayısı belirlenemedi.";
break;
case "Draft":
result = $"Tüm taslak olan {entity} öğeleri başarıyla getirildi.";
break;
}
return result;
}
}
// Aşağıda tek kullanıcıyı çekerken bir DataResult dönüyorum. Kullanıcı bulunursa ResultStatus.Succes ile beraber Mesajı dönüyorum. Bulamazsam ResultStatus.Error dönüyorum.
public async Task GetAsync(Guid id)
{
var wGetted = await _memberManager.FindByIdAsync(id.ToString());
if (wGetted != null)
{
return new DataResult(ResultStatus.Success, new UserDto
{
User = wGetted,
Message = Messages.Info("User", "Get", wGetted.UserName)
});
}
return new DataResult(ResultStatus.Error, new UserDto
{
User = null,
Message = Messages.Info("User", "NotFoundSingle")
});
}
//DataResult ile data içeren result dönüyorum. Bir de sadece result var. Onda da data içermeyenleri dönüyorum. Mesela kullanıcı silindi vs.
public async Task HardDeleteAsync(Guid id)
{
var user = await _memberManager.FindByIdAsync(id.ToString());
if (user != null)
{
if (user.IsDeleted == false)
{
user.IsDeleted = true;
}
var hardDeleted = await _memberManager.DeleteAsync(user);
if (hardDeleted.Succeeded)
{
return new Result(ResultStatus.Success, Messages.Info("User", "HardDelete", user.UserName));
}
}
return new Result(ResultStatus.Error, Messages.Info("User", "NotFoundSingle"));
}
Çözüm bulabildin mi ?
@@kagancosar7470 Response içinde dönmem gerekir diye düşünüyorum. ama henüz tam netleştirmedim.
@@kagancosar7470 Çözdüm. Çok da güzel çalışıyor.
//Response içerisine geriye mesaj dönmek istediğim için onu ekledim. Bir de ne oluşturduysam onu geri dönmek istedim.
public class CreateCategoryCommandResponse
{
public virtual string Message { get; set; }
public Category Category { get; set; }
}
//Handlerım da aşağıdaki gibi. En altta Response metodu dönüyorum. İçine de eklenen kategori nesnesini ve mesajımı yolluyorum.
public async Task Handle(CreateCategoryCommandRequest request, CancellationToken cancellationToken)
{
if (request.Name != null && request.Name.Length > 0)
{
var category = await _categoryCrudRepository.AddAsync(new()
{
Name = request.Name.ToLower().Trim(),
Ikon = request.Ikon.Trim(),
Description = request.Description.Trim()
});
var result = await _categoryCrudRepository.SaveAsync();
if (result == 1)
{
return new CreateCategoryCommandResponse()
{
Category = category,
Message = Messages.Info("Category", "Add", category.Name)
};
}
else
{
return new CreateCategoryCommandResponse()
{
Category = null,
Message = Messages.Info("Category", "NotAdded", request.Name)
};
}
}
return new CreateCategoryCommandResponse()
{
Category = null,
Message = Messages.Info("Category", "Empty")
};
}
//Messages sınıfım aşağıdaki gibi. Dönecek mesaj şöyle "category.Name adlı kategori başarıyla eklendi". Switch case yapısı kurdum ki dinamik olsun. Tek bir sınıfla tüm mesajları globalleştirmiş oldum böylece. Aşağıda sadece add metodu için yazdım. Diğer metotlar da var ama ben burada kısaca yazdım.
public static string Info(string entity, string method, string value = null, string currentName = null)
{
string result = "";
switch (entity)
{
case "Category":
entity = "kategori";
break;
}
switch (method)
{
case "NotAdded":
result = $"{value} isimli {entity} eklenemedi.";
break;
case "Empty":
result = $"Zorunlu alanlar boş bırakılamaz.";
break;
case "Add":
result = $"{value} isimli {entity} başarıyla eklendi.";
break;
}
return result;
}
Merhaba hocam,
Application katmanında ServiceRegistration içinde services.AddMediatR(typeof(ServiceRegistration)); kullandım anlattığınız gibi. Localde uygulamayı çalıştırdığımda problem olmuyor. Ama bir IIS'e kurduğumda kısaca ("Register your handlers with the container") hatası alıyorum. Handler içinde kullandığım IRepository karşılığında kullanacağı Repository'i çözemiyor olabilir mi ? Nasıl bir yol izlemeliyim ?
Hocam tekrar merhaba,
Hatayla ilgili tüm araştırmalarımda service.AddMediatR kısmıyla ilgili çözümlerle karşılaşınca ben de oraya odaklandım. Ama aslında tamamen alakasız bir durummuş. Aslında DbContext'i olması gerektiği gibi oluşturamıyormuş. Çünkü Configration sınıfında yol olarak "../../Presentation/Project.Api" verdik. Burada appsettings.js yi arıyor. Projeyi IIS'e publish olarak attığımda appsettings.json aslında aynı dizinde. 2 üst klasöre çıkıp Presentation klasörünü ve içindeki Poject.Api klasörünü bulamadığı için DbContext ayağa kalkamıyor.
Hatayla ilgili gereksiz vaktinizi aldıysam kusura bakmayın..
Hocam CreateProductValidator kısmını da güncellememiz gerekmiyor mu?
Daha oraya geleceğiz.
Hocam, ilk action'ı sizin yaptığınız gibi kodladım ama client tarafında veriler geliyor ama sayfada gözükmüyor. Kodları eski hale getirince sayfada gözüküyor. Hatayı aradım ama bulamadım bu problem neyden kaynaklanıyor olablir?
Merhabalar ben de aynı hatayla karşılaştım çözümü bulabildiniz mi ?
Tamamdır hocam sorunu buldum değişkenler ile ilgili hata varmış API kısmında ne ise Angular kısmında da aynısını yaparsanız sorun gideriliyor. Çözemediyseniz bilginiz olsun.
Hocam MediatR 12.1.1 sürümündne sonra hata veriyor ve bunu hiçbir şekilde çözemedim. Yalvarırım yardımcı olun
11 sürümünü kullanın
@@feyzakeskin1364 işin garibi kullandirtmiyor onda da hata veriyor
@@ugurcandan843 sorunu çözdün mü? bazı arkadaşlar ServiceRegistration sınıfına, services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(ServiceRegistration).Assembly)); yazıldığında çözüldüğünü söylemiş
Hocam .Net8 ile gelen yenilikler var mı? Varsa nelerdir? Bunlarla ilgili video cekme planiniz var mi?
Evet, uygun bi zaman çekeceğim ama erken bilgi için bloğumu takip edebilirsiniz.
@ takipteyim hocam lakin video baska tabii. Bekliyoruz emeginize saglık