Teknik Konular - Generic Repository Pattern ve Entity Framework - ihan3t

ihan3t

Kadim Üye
7 Şub 2012
5,018
22

Selamlar, bu yazımda sizlere Repository Design pattern ve Entity Framework ile kullanımını anlatacağım.

Konunun amacı entity frameworkün kendisini anlatmak değil, repository pattenr i entity framework ile birlikte kullanabilmek. O yüzden detaylı entity framework bilgisi arıyorsanız yanlış konudasınız. Temel entity framework bilginiz olduğunu varsayıyorum.


Nedir bu repository pattern?

Yazılım geliştirirken, tasarım aşamasında birçok tasarım desenleri kullanılır. Creational, behavioral, structural temel başlıkları altında patternler mevcuttur. Bunları araştırmanızın size çok büyük katkıları olacaktır. Özellikle GOF design patterns.

Repository nin Martin Fowler ın sözleriyle anlamına bir bakalım :

"Repository: Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects."

Domain objelerine erişim için kullanılan bir arayüz olarak bahsedebiliriz kendisinden.

Database işlemlerinde yaptığımız temel işlemleri CRUD olarak özetleyebiliriz. Yani Create, Read, Update, Delete.

Bu işlemleri tek bir interface üzerinde tanımlayıp, bunları domain objelerine özel implement eden concrete nesnelerimiz olursa, bunun adı repository olmuş olur.

Sözel kısmı geçip kod tarafında implementasyonuna bakalım.

Entity sınıflarımız ve DbContext sınıfımız:

Kod:
    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Password { get; set; }
        public string Role { get; set; }
    }

    public class Product
    {
        public int Id { get; set; }
        public string ProductName { get; set; }
    }

    public class MyDbContext : DbContext
    {
        public TurDbContext(DbContextOptions options) : base(options)
        {
            
        }

        protected override **** OnModelCreating(ModelBuilder modelBuilder)
        {
            
        }

        public DbSet<User> Users { get; set; }
        public DbSet<Product> Products { get; set; }
    }

IRepository interface imiz :

Kod:
    public interface IRepository<T> where T : class
    {
        T Create(T t);
        T Get(int id);
        IEnumerable<T> List(); 
        IQueryable Query(Expression<Func<T,bool>> query);
        IQueryable Query();
        **** Update(T t);
        **** Delete(T t);
    }

Concrete UserRepository sınıfımız :

Kod:
    public class UserRepository : IRepository<User>
    {
        private readonly TurDbContext _context;
        private readonly DbSet<User> _dbSet;
        
        public UserRepository(TurDbContext context)
        {
            _context = context;
            _dbSet = context.Set<User>();
        }

        public User Create(User t)
        {
            _dbSet.Add(t);
            _context.SaveChanges();
            return t;
        }

        public User Get(int id)
        {
            return _dbSet.Find(id);
        }

        public IEnumerable<User> List()
        {
            return _dbSet.ToList();
        }

        public IQueryable<User> Query(Expression<Func<User, bool>> query)
        {
            return _dbSet.Where(query);
        }

        public IQueryable<User> Query()
        {
            return _dbSet.AsQueryable();
        }

        public **** Update(User t)
        {
            _dbSet.Update(t);
            _context.SaveChanges();
        }

        public **** Delete(User t)
        {
            _dbSet.Remove(t);
            _context.SaveChanges();
        }
    }

Elimizde bulunan bu classlarımızla artık uygulamamız içerisinden User ile ilgili database işlemlerini yapabiliriz.

Repository nin uygulamamızda kullanımını göstermek için örnek bir servis katmanı kodunu veriyorum :

Kod:
public class UserService
    {
        private readonly UserRepository _repository;

        public UserService(UserRepository  repository)
        {
            this._repository = repository;
        }

        public IEnumerable<User> GetUsers()
        {
            var Users = _repository.List();
            return Users;
        }

        public User GetUser(int id)
        {
            User u = _repository.Get(id);
            return u;
        }

        public User CreateUser(User User)
        {
            User u = _repository.Create(User);
            return u;
        }

        public List<User> Query(QueryUserRequest request)
        {
            IQueryable<User> query = _repository.Query();

            if (request.Id.HasValue)
            {
                query = query.Where(User => User.Id == request.Id);
            }

            if (!string.IsNullOrEmpty(request.UserName))
            {
                query = query.Where(User => User.UserName == request.UserName);
            }

            return query.Select(u => new User
            {
                Id = u.Id,
                UserName = h.UserName,
             }).ToList();
        }
    }

Burada service katmanımız mevcut. N-tier mimariyi araştırırsanız service katmanının amacını öğrenebilirsiniz. Konumuz n-tier ve web uygulama mimarisi olmadığı için şimdilik o konunun ayrıntılarına girmiyorum belki başka bir yazımın konusu da bu olur.


Verdiğim örneklerde gördüğünüz gibi, classlarımız bir önceki konum olan http://www.turkhackteam.org/c-j-vb-...ik-konular-dependency-injection-1-ihan3t.html dependency injection metodu ile inject ediliyor.

.Net Core tarafında dependency injection işlemi startup dosyasında ConfigureServices metodu içerisinde yapılıyor.

Örnek olarak :

Kod:
public **** ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            services.AddDbContext<MyDbContext>(opt => opt.UseInMemoryDatabase());
            
            services.AddScoped<UserService, UserService>();
            services.AddScoped<IRepository<User>, UserRepository>();
        }

Artık ihtiyaç durumunda yeni bir db işlemi gerektiğinde sadece UserRepositoryXXX adında bir class oluşturup IRepository interface inden kalıtım alıp, IoC container (ConfigureServices metodu içerisindeki services.AddScope kısmı) bölümünde ismini vermemiz yeterli. Uygulamamız hayatına hiçbir sorun olmadan devam edecektir.


Buraya kadar her şey çok güzel. Fakat bizim başka entity lerimiz de mevcut. Bunların her birisi için aynı işlemi yapan farklı repositoryler mi oluşturacağız?

Hayır. Bu problemi bizim için çöze Generic Repository Pattern e başvuracağız.

Yukarıda gösterdiğim bütün kodlar aynı kalsın. Sadece UserRepository sınıfını aşağıdaki hale getirelim:

Kod:
public class GenericRepository<T> : IRepository<T> where T : class
    {
        private TurDbContext _context;
        private DbSet<T> _dbSet;
        
        public GenericRepository(TurDbContext context)
        {
            this._context = context;
            this._dbSet = context.Set<T>();
        }

        public T Create(T t)
        {
            _dbSet.Add(t);
            _context.SaveChanges();
            return t;
        }

        public T Get(int id)
        {
            T t = _dbSet.Find(id);
            return t;
        }

        public IEnumerable<T> List()
        {
            List<T> list = _dbSet.ToList();
            return list;
        }

        public IQueryable<T> Query(Expression<Func<T, bool>> query)
        {
            return _dbSet.Where(query);
        }
        
        public IQueryable<T> Query()
        {
            return _dbSet.AsQueryable();
        }

        public **** Update(T t)
        {
            _dbSet.Update(t);
            _context.SaveChanges();
        }

        public **** Delete(T t)
        {
            _dbSet.Remove(t);
            _context.SaveChanges();
        }

    }

UserService sınıfında repository mizi şu şekilde değiştirelim

Kod:
        private readonly IRepository<User> _repository;

        public UserService(IRepository<User> repository)
        {
            this._repository = repository;
        }

ve dependency injection kısmını şu şekilde değiştirelim:

Kod:
    services.AddScoped<IRepository<User>, GenericRepository<User>>();

Artik bu sınıfı her entity için repository olarak kullanabiliriz.

Bizim var olan Product entity miz için bir ProductService yazmak istesek tek yapmamız gereken service sınıfını açıp UserService kodlarının aynısını yazmak ve dependency injection kısmında

Kod:
    services.AddScoped<ProductService, ProductService>();
    services.AddScoped<IRepository<Product>, GenericRepository<Product>>();

dememiz yeterli. Böylelikle product sınıfı için özel olarak tek satır database sorgusu yapmadan bütün CRUD işlemlerimiz kullanıma hazır halde.

Onlarca entity nesneleriniz olduğunu düşünürsek Generic Repository Pattern bizi oldukça fazla bir iş yükünden kurtarıyor.

Design patternler yazılım dünyasında önemli bir yer kaplıyor. Hele ki kurumsal bir proje üzerinde çalışıyorsanız olmazsa olmazlardandır diyebilirim. Öğrenmek ufkunuzu açacak ve kod yazma standartınızı yükseltecektir emin olun.

Herkese iyi forumlar, ihan3t.
 
Son düzenleme:

AJEXA

Adanmış Üye
22 Haz 2013
6,859
3
Jüpiter
Anlamama rağmen saçma sapan konular güncelden düşsün diye eline sağlık üstad :)

Ben anlamayabilirim, ama anlayan biri çıkacaktır
 

1071malazgirt

Uzman üye
26 Haz 2015
1,781
8
Trafo
Makaleyi Okurken Türkçe kelime arayışına girdim diyebilirim. Fazla bilgim olmayınca ağır geldi. Ama emek var Elinize sağlık. Forumda alışılmışın dışında makaleler görmek güzel
 

ihan3t

Kadim Üye
7 Şub 2012
5,018
22
Anlamama rağmen saçma sapan konular güncelden düşsün diye eline sağlık üstad :)

Ben anlamayabilirim, ama anlayan biri çıkacaktır

Makaleyi Okurken Türkçe kelime arayışına girdim diyebilirim. Fazla bilgim olmayınca ağır geldi. Ama emek var Elinize sağlık. Forumda alışılmışın dışında makaleler görmek güzel

Yine araştırılacak yüzlerce şey, yer imlerime ekledim eline sağlık patron

Anlamadığınız, kafanızı karıştıran herhangi bir şeyi sorabilirsiniz dostlar.

Bu konular çok teknik, design pattern konusudur ilk kez gören birisine karışık gelmesi çok normal.

İçerisinde bulunan yabancı terimlere aldırmayın, entity dediğimiz şey model class larımız. Repository bu yapının adı, n-tier mimari katmanlı mimari demek.. Bunları araştırırsanız çok faydalı bilgiler edinirsiniz ve projelerinizde birçok kolaylık sağladığını görürsünüz.

 

VirtualSystem

Katılımcı Üye
19 Ağu 2017
411
0
Anlamadığınız, kafanızı karıştıran herhangi bir şeyi sorabilirsiniz dostlar.

Bu konular çok teknik, design pattern konusudur ilk kez gören birisine karışık gelmesi çok normal.

İçerisinde bulunan yabancı terimlere aldırmayın, entity dediğimiz şey model class larımız. Repository bu yapının adı, n-tier mimari katmanlı mimari demek.. Bunları araştırırsanız çok faydalı bilgiler edinirsiniz ve projelerinizde birçok kolaylık sağladığını görürsünüz.


Hocam işin teknik kısımları hakkında kitaplar hariç pek bir makale bulduğum söylenemez, işin felsefi boyutunu herkes yorumlar herkes anlatır... Ancak teknik boyutunu anlatabilen nadide insanlar vardır sizde onlardan birisiniz. Ellerinizi sağlık.
 

ihan3t

Kadim Üye
7 Şub 2012
5,018
22
Hocam işin teknik kısımları hakkında kitaplar hariç pek bir makale bulduğum söylenemez, işin felsefi boyutunu herkes yorumlar herkes anlatır... Ancak teknik boyutunu anlatabilen nadide insanlar vardır sizde onlardan birisiniz. Ellerinizi sağlık.

Aradaığın belirli bir konu varsa makale bulamıyorsan konu başlığını söyle bilgim varsa anlatayım ilgili makeleleri de göndereyim dostum..

Yeter ki insanlar bir şeyler öğrenmek istesin, gerçekten bu alanla ilgilensin...
 

xenceri

Uzman üye
16 Ağu 2010
1,948
0
İstanbul
Çok teşekkürler hocam oldukça yararlı bir makale olmuş. Galiba bu pattern'lerin iyice yerleşmesi için bol bol pratik yapmam lazım.

Şimdi hocam şöyle bir sorun daha var. GenericRepository sınıfını hazırladım. _repository.Repository<ClassAdi>() ile dediğiniz CRUD işlemlerine tek sınıf üzerinden erişebiliyorum. Peki bir WebApi servisini nasıl generic hale getirebilirim. Kullanıcıların bir url üzerinden bu genericrepository'i kullanmasını istiyorum. Şöyle ki daha açıklayıcı olmak gerekirse,

public class GenericController<T> : ApiController where T : class
{
...
...
...
[HttpPost]
[Route("api/generic/TumunuGetir")]
public IEnumerable<T> TumunuGetir()
{
return _repository.Repository<T>().GetAll();
}
}

Şöyle bir mantıkla webapi üzerinden işlemleri generic yapmak mümkün mü?
...
...
client.BaseAddress = new Uri("http://localhost:53698/");
string json = SerializeIgnoreNull<Kullanici>.ConvertToString(new Kullanici());
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = client.PostAsync("api/generic/TumunuGetir", content).Result;
Tabi bu kod 404 hatası veriyor.
Bunun mantığı nasıl olur. Tek tek tüm tablolar için bir controller mı oluşturmam gerekiyor. Eğer öyleyse o zaman GenericRepository'i oluşturmaktaki amaç nedir her halükarda her tablo için tekrar controller oluşturmak zorunda kaldık. Tek kolaylığı
public IEnumerable<Kullanici> TumunuGetir()
{
return _repository.Repository<Kullanici>().GetAll();
}
şeklinde erişebilmek oldu. WebApi üzerinden T sınıfını yollamanın bir yolu yok mu? Url üzerinden T'yi algılasa ve ona göre genericrepositoryden işlemi yapıp sonucu yollasaydı tadından yenmezdi :)
 

ihan3t

Kadim Üye
7 Şub 2012
5,018
22
Çok teşekkürler hocam oldukça yararlı bir makale olmuş. Galiba bu pattern'lerin iyice yerleşmesi için bol bol pratik yapmam lazım.

Şimdi hocam şöyle bir sorun daha var. GenericRepository sınıfını hazırladım. _repository.Repository<ClassAdi>() ile dediğiniz CRUD işlemlerine tek sınıf üzerinden erişebiliyorum. Peki bir WebApi servisini nasıl generic hale getirebilirim. Kullanıcıların bir url üzerinden bu genericrepository'i kullanmasını istiyorum. Şöyle ki daha açıklayıcı olmak gerekirse,



Şöyle bir mantıkla webapi üzerinden işlemleri generic yapmak mümkün mü?

Tabi bu kod 404 hatası veriyor.
Bunun mantığı nasıl olur. Tek tek tüm tablolar için bir controller mı oluşturmam gerekiyor. Eğer öyleyse o zaman GenericRepository'i oluşturmaktaki amaç nedir her halükarda her tablo için tekrar controller oluşturmak zorunda kaldık. Tek kolaylığı
public IEnumerable<Kullanici> TumunuGetir()
{
return _repository.Repository<Kullanici>().GetAll();
}
şeklinde erişebilmek oldu. WebApi üzerinden T sınıfını yollamanın bir yolu yok mu? Url üzerinden T'yi algılasa ve ona göre genericrepositoryden işlemi yapıp sonucu yollasaydı tadından yenmezdi :)


Biraz yanlış anlaşılma olmuş..

Repository, n-tier yapıda Data Access Layer olarak düşünülebilir.

Restful gidiyorsan, kesinlikle tek bir kontrollerdan her şeye erişmemelisin. O zaman bir anlamı kalmaz.

Senin dediğin ile benim anlattığım konunun kapsamları farklı. Sen SOAP veya RPC gibi bir yapıdan bahsediyorsun. Tek bir url üzerinde farklı metodlar çağırmak vs vs..

Repository Domain Driven Design konusu kapsamında. Bu kavramları araştırırsan daha detaylı anlatımları bulabilirsin. Kapsamlı konulardır.

Bir restful api yazıyorsan, users / products / orders diye farklı endpointlerin varsa 3 farklı controller ve service yazacaksın demektir. Bu 3 farklı service tek bir altyapı olan GenericRepository ile tüm db işlemlerini gerçekleştirebiliyor olur.


 
Üst

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