Generic Repository Pattern
Husamettin Elalmis
Last updated
Husamettin Elalmis
Last updated
Generic Repository Pattern
Merhaba arkadaşlar, bu yazıda sizlere Generic Repository Pattern kavramından bahsecedeğim.
Repository Pattern Nedir?
Veriye erişim ve yönetimi kapsamında, tek bir noktadan yönetilebilen yapılara Repository Pattern diyoruz.
Repository Pattern kullanmak, projelerimizin rahat ve kolay ölçeklenebilmesini, büyüyebilmesini sağlar.
Repository Patternin kullanılmadığı durumlarda, yazılan kodlar tekrara girme eğilimine girer ve kod spaghetti (baştan aşağıya karışık) yapıya doğru gider. Yönetilemez hale gelir.
Generic Repository Pattern Nedir?
Generic, "tipi değişen" demektir.
Generic repository pattern dediğimizde, repositorymizin içerisindeki kullanılan Tip nesnesinin değişebileceğini anlarız. Yani, merkezi bir repository yapımız var iken, bu merkezi repository desenimiz, t_Users tipi alabileceği gibi t_Customers tipi de alabilir olduğunu anlarız.
Bu tarz generic yapılar, mimari anlamda projeye büyük kolaylıklar sağlar.
Generic yapılar, mimari kodlamada olmazsa olmazlardandır, hayal gücü ile doğru orantılı olarak genişlemeye müsaiddir.
Senaryo MSSQL Tablo Yapısı
PhoneBookDB veritabanı oluşturdum
t_Users tablosu oluşturdum
Amacımız, bu DBye, Generic Repository Pattern yaklaşımı ile ulaşmak olacak.
Program
PhoneBookDBContext.cs
using GenericRepositoryArge.Base;
using Microsoft.EntityFrameworkCore;
namespace GenericRepositoryArge.Context
{
// dbContextimiz public class PhoneBookDbContext : DbContext
{
// constructorumuz public PhoneBookDbContext(DbContextOptions<PhoneBookDbContext> options) : base(options)
{
}
// tablolarımız public DbSet<t_Users> t_Users { get; set; }
}
}
IEntityBase.cs
using GenericRepositoryArge.Context;
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace GenericRepositoryArge.Base
{
// base interfaceimiz public interface IEntityBase
{
// buraya ihtiyacımıza göre eklemeler yapabiliriz
}
// generic classımız public class EntityBase<TKey> : IEntityBase
{
// primary key kolonumuz, türü generic
[Key]
public TKey Id { get; set; } // tüm tablolarda ortak olan kolonlar public DateTime CreatedAt { get; set; } // .. varsa diğerleri
}
// t_users tablomuz, primary key türü integer public class t_Users : EntityBase<int>
{
// tablo kolonlarımız public string UserName { get; set; } // .. varsa diğer kolonlar
}
// generic interfaceimiz, tablo ve primarykey türü alır, baseEntityden türemek zorundadır // tüm tablolar bu interface üzerinden türeyebilir public interface IRepositoryBase<TEntity, TKey> where TEntity : EntityBase<TKey>
{
// tüm kayıtları döndürecek olan methodumuz, expression filter parametre destekli, IQueryable döndürüyoruz
IQueryable<TEntity> GetAll(Func<TEntity, bool> filter = null);
// Idye göre kayıt getir (primary key türü generic)
Task<TEntity> GetById(TKey id);
// tabloya insert eden method
Task<TEntity> Insert(TEntity entity);
// satırı update eden method
Task<TEntity> Update(TEntity entity);
// tablodan veri silen method
Task<TEntity> Delete(TEntity entity);
// .. varsa diğer methodlar
}
// repository classımız public class RepositoryBase<TEntity, TKey> : IRepositoryBase<TEntity, TKey> where TEntity : EntityBase<TKey>
{
// dbContext nesnemiz, bu repository bu context ile muhatap olacak private readonly DbContext context; // constructor public RepositoryBase(DbContext context)
{
this.context = context;
}
// generic delete methodumuz, virtual yapmamın sebebi ezilebilir olmasını istememdir public virtual Task<TEntity> Delete(TEntity entity)
{
context.Set<TEntity>().Remove(entity);
context.SaveChanges();
return Task.FromResult(entity);
}
// generic GetAll methodumuz, virtual yapmamın sebebi ezilebilir olmasını istememdir public virtual IQueryable<TEntity> GetAll(Func<TEntity, bool> filter = null)
{
// performanslı getirsin diye AsNoTracking kullanıldı return (filter == null) ? context.Set<TEntity>().AsNoTracking() : context.Set<TEntity>().AsNoTracking().Where(filter).AsQueryable();
}
// generic getById methodumuz, virtual yapmamın sebebi ezilebilir olmasını istememdir public virtual Task<TEntity> GetById(TKey id)
{
return Task.FromResult(context.Set<TEntity>().AsNoTracking().FirstOrDefault(x => x.Id.Equals(id)));
}
// generic insert methodumuz, virtual yapmamın sebebi ezilebilir olmasını istememdir public virtual async Task<TEntity> Insert(TEntity entity)
{
await context.Set<TEntity>().AddAsync(entity); await context.SaveChangesAsync(); return entity;
}
// generic update methodumuz, virtual yapmamın sebebi ezilebilir olmasını istememdir public virtual async Task<TEntity> Update(TEntity entity)
{
context.Set<TEntity>().Update(entity);
await context.SaveChangesAsync(); return entity;
}
}
// t_Users tablosuna ilişkin arayüz repositorymiz. Tüm repositoryler bundan türetilir. public interface IUserRepository : IRepositoryBase<t_Users, int>
{
}
// t_Users repositorymiz. Primary key tipi integer, tablomuz t_users anlamındadır. public class UserRepository : RepositoryBase<t_Users, int>, IUserRepository
{
// constructorumuz public UserRepository(PhoneBookDbContext context) : base(context)
{
// bu repository tanımlanırken buraya context parametre geçilir, bu context bu repository için muhatap olur.
}
}
}
Program.cs
using GenericRepositoryArge.Base;
using GenericRepositoryArge.Context;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace GenericRepositoryArge
{
class Program
{
static void Main(string[] args)
{
Test();
Console.WriteLine("ok");
Console.ReadLine();
}
private async static void Test()
{
string conStr = @"Data Source=DESKTOP-PBV06FI\Server2014; Initial Catalog=PhoneBookDB; Persist Security Info=True; Integrated Security=SSPI;pooling=true;"; // dependency injection setupumuz // SQL server kullanacağımızı söylüyoruz, PhoneBookDbContext'i kullanacağız, // interface'e karşılık hangi Repositorynin ayağa kalkacağını belirliyoruz var serviceProvider = new ServiceCollection()
.AddDbContext<PhoneBookDbContext>(x => x.UseSqlServer(conStr))
.AddScoped<IUserRepository, UserRepository>()
.BuildServiceProvider();
// userRepository oluştur var userRepo = serviceProvider.GetService<IUserRepository>(); // entity oluştur
t_Users entity = new t_Users() { UserName = "Hüsamettin4", CreatedAt = DateTime.Now };
Console.WriteLine("Kayıt eklendi");
// kaydet var res2 = await userRepo.Insert(entity); // verileri getir var res = await userRepo.GetAll().ToListAsync(); // verileri yaz foreach (var item in res)
{
Console.WriteLine($"{item.Id} {item.UserName} {item.CreatedAt}");
}
}
}
}
Sonuç
Bu dokumanda Generic Repository Pattern yapısını uygulamalı görmüş olduk
Dependency Injection kavramını kullanmış olduk
Her tablo için Repository oluşturulabilir, tüm repositoryler merkezi yerden beslenir (Generic Repository Pattern)
Bu yapı, her türlü projede mimari olarak kullanılabilir, ölçeklenebilir, genişletilebilir.
MSSQL ve POSTGRE providerlari ile çalışılabilir
ORACLE entegrasyonu yapılabilir (İlave kodlamalar ile)
Ado.Net mimarisi entegre edilebilir
Dapper mimarisi entegre edilebilir
Saygılarımla,
Hüsamettin ELALMIŞ – 11.06.2022
husamettin.elalmis@arksoft.com.tr