İçeriğe geç

Generic Repository ve Unit of Work Kullanarak Temel Bir Infrastructure Tasarlamak

Merhaba arkadaşlar, bu makalemde sizlerle Generic Repository ve Unit of Work kurumsal tasarım kalıplarını uygulayarak temel bir alt yapı (infrastructure) tasarlayacağız. Tasarlama sırasında Entity Framework’den yararlanarak code first yaklaşımı ile geliştireceğiz. Tabi ki alt yapımız ORM bağımsız (independent) olacak.

Alt yapımızı geliştirirken makul bir seviyede sıkı sıkıya bağlı (tight coupled) olmadan  gevşek bağlanmış (loosely coupled) bir şekilde geliştirmeye çalışacağız ki, gerek unit test‘ler olsun gerekse de ileride geliştireceğimiz bir modül için kırılma noktasına gelmeden, esnek bir şekilde entegre edebilmek için dependency inversion (bağımlılığın ters çevirilmesi) prensibinden faydalanarak alt yapımızı esnek ve tekrar kullanılabilir bir şekilde tasarlayacağız.

Alt yapımızı tasarlamaya başlamadan önce bu iki kurumsal tasarım kalıbını bir tanımlayalım.

Repository Pattern Nedir?

Bu desenin temelinde yatan asıl amaç;

Veritabanına veri ekleme, güncelleme ve okuma gibi CRUD (Create Read Update Delete) işlemlerimiz için oluşturmuş olduğumuz kodların tekrar kullanılabilirliğini sağlamaktır.

Yazılım geliştirmede, belkide en önemli prensiplerden birisi olan DRY (Don’t repeat yourself) yani kendini tekrarlama prensibine göre kod tekrarlarından sakınmak gerekmektedir. Repository Patterni ise bize bunu sağlamaktadır. Oluşturduğumuz bir sınıf ile tüm tablolar için CRUD işlemlerimizi yapabilmemizi sağlamaktadır ve kod tekrarını önlemektedir. Aksi durumda bir düşünsenize, 30 tablolu bir yapımız mevcut ve her biri için CRUD işlemleri yapmamız gerektiğini? Vay anam vay…

RepositoryDiagram

Unit of Work Pattern Nedir?

Unit of Work ise bize;

Veritabanı ile yapılacak olan tüm işlemleri, tek bir kanal aracılığı ile gerçekleştirme ve hafızada tutma işlemlerini sunmaktadır. Bu sayede işlemlerin toplu halde gerçekleştirilmesi ve hata durumunda geri alınabilmesi sağlamaktadır.

Unit of Work deseni tek başına kullanılabileceği gibi, Repository kurumsal tasarım deseni ile (ki makalemiz konusudur aşağıda beraber kullanımını göreceğiz) veya Identity Map kurumsal tasarım deseni ile de kullanılabilir.

Identity Map: ORM geliştirirken güncelleme (update) işlemleri sırasında veritabanından çekilmiş olan nesnenin, sadece değişime uğrayan alanlarını güncelleyebilmemizi sağlayan bir desendir.

Unit of Work deseninde ise, işlemleri hafızada tutma özelliğinden bahsetmiştik. Identity Map deseni işte bu noktada implemente edilebilir.

unitofwork

Yukarıdaki şemayı da incelediğimizde göreceğimiz üzere ilk başta veritabanına direkt erişimi görüyoruz klasik programlamada. İkinci kısımda ise bir tarafta Entity Framework için hazırlanmış olan Unit of Work deseni ile ilgili Repository’ler aracılığı ile veritabanına erişim var. Bu sayede Unit Test’ler için olan Mocklanmış sınıflar aracılığı ile test bir veritabanına veya in-memory hazırlanmış veri alanına erişimini görüyoruz.

Mock Object: Unit testler yaparken uygulamamızın tümünü ayakta tutmak/tutabilmek zor olacaktır bir hayli. Bunun yerine uygulamamızın test ortamında çalışabileceği kadar fonksiyonları içeren sınıflar hazırlanır. Bu tür sınıflara Mock Object denir.

Şimdi Repository ve Unit of Work kurumsal kalıplarının ne olduğunu anladığımıza göre, örneğimize geçelim ve beraber kullanımlarını detaylıca incelemeye başlayalım.

Örneğimiz gereği basitleştirilmiş temel bir alt yapı tasarlayacağız. Bu alt yapımız Entity Framework‘den yararlanarak code first yaklaşımı ile geliştireceğiz ve Repository ve Unit of Work desenlerini ise maksimum seviyede izole ederek, gerek Entity Framework ve MSSQL ile gereksede Mongo DB ilede çalışabilir bir şekilde modüler geliştirmeye çalışacağız.

Örneğimiz basit bir Blog sistemi olacak ve User, Article ve Category sınıflarından oluşacak.

İşe RepveUOW isminde boş bir solution oluşturarak içerisine ise RepveUOW.Data.Model isminde bir sınıf kütüphanesi (class library) ekleyerek başlıyorum.

Burada isminden de anlaşılabileceği üzere Poco sınıflarımız yani modellerimiz yer alacak. Bu sayede uygulamamız içi bu entity nesnelerimiz orm bağımsız bir şekilde gelişiyor olacak.

Model katmanı içerisine ilk başta tüm entity nesnelerimiz için ortak olan CreatedDate özelliğini kalıtım yolu ile aktarabilmek için ModelBase isminde bir soyut sınıf oluşturuyoruz.

using System;

namespace RepveUOW.Data.Model
{
    public abstract class ModelBase
    {
        public DateTime CreatedDate { get; set; }
    }
}

Soyut sınıfımız hazır olduğuna göre şimdi entitylerimizi oluşturmaya başlayabiliriz.

User sınıfı ile başlıyoruz.

using System;
using System.Collections.Generic;

namespace RepveUOW.Data.Model
{
    public class User : ModelBase
    {
        public User()
        {
            this.Articles = new List<Article>();
        }

        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }

        // Fluently bir şekilde ilişkileri kullanabilmemiz için tanımlıyoruz.
        public virtual ICollection<Article> Articles { get; set; }
    }
}

Category sınıfı ekliyoruz.

using System.Collections.Generic;

namespace RepveUOW.Data.Model
{
    public class Category : ModelBase
    {
        public Category()
        {
            this.Articles = new List<Article>();
        }

        public int Id { get; set; }
        public string Name { get; set; }

        // Relations
        public virtual ICollection<Article> Articles { get; set; }
    }
}

Şimdide Article sınıfını ekliyoruz ve Model katmanımızı tamamlamış oluyoruz.

using System;

namespace RepveUOW.Data.Model
{
    public class Article : ModelBase
    {
        public int Id { get; set; }
        public int CategoryId { get; set; }
        public int UserId { get; set; }

        public string Title { get; set; }
        public string Content { get; set; }

        // Relations
        public virtual Category Category { get; set; }
        public virtual User User { get; set; }
    }
}

Modellerimizi oluştururken gördüğünüz gibi ilişkileri kullanabilmek için altta // Relations skobunda tanımladık. Bu ilişkileri daha sonra ilgili Context sınıfı içerisinde context ayağa kalkarken tanımlayacağız.

Model katmanını tasarladığımıza göre şimdi Data katmanını oluşturmaya başlayabiliriz.RepveUOW.Data isminde bir sınıf kütüphanesi daha oluşturuyoruz ve References kısmında Manage Nuget Packages sekmesine girip Entity Framework‘ü install ettikten sonra tekrar References kısmından Add Reference seçeneğini seçerek Projects tabından RepveUOW.Data.Model katmanını seçerek referans olarak ekliyoruz.

Referans işlemlerini tamamladığımıza göre RepveUOW.Data katmanı içerisinde Context isminde bir klasör tanımlayıp EFBlogContext sınıfını oluşturuyoruz.

EFBlogContext sınıfı bizim entity framework için kullanacak olduğumuz context’imiz olacak.

using System.Data.Entity;
using RepveUOW.Data.Model;

namespace RepveUOW.Data
{
    public class EFBlogContext : DbContext
    {
        public EFBlogContext()
            : base("BlogContext")
        {
        }

        public DbSet<User> Users { get; set; }
        public DbSet<Category> Categories { get; set; }
        public DbSet<Article> Articles { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // İlişkileri kuruyoruz one-to-many olarak.
            modelBuilder.Entity<Article>()
                .HasRequired<Category>(x => x.Category)
                .WithMany(x => x.Articles)
                .HasForeignKey(x => x.CategoryId);

            modelBuilder.Entity<Article>()
                .HasRequired<User>(x => x.User)
                .WithMany(x => x.Articles)
                .HasForeignKey(x => x.UserId);

            base.OnModelCreating(modelBuilder);
        }
    }
}

Code first yaklaşımı ile geliştirmek istediğimizden EFBlogContext sınıfımızı System.Data.Entity namespace’inde bulunan DbContext sınıfından türetiyoruz. EFBlogContext’in constructor’ında DbContext sınıfının constructur’ına connection string’in ismini gönderiyoruz. Daha sonra bu connection string ismini Data katmanımızı kullanacak olan sunum katmanının ilgili App.config veya Web.config içerisinde tanımlıyor olacağız.

DbSet’ler aracılığı ile ilgili poco sınıflarımızı tanımladık ve modelimiz oluşurken ilişkileri tanımlayabilmemiz için OnModelCreating metotunu ezerek ilgili ilişkileri tanımladık. Fluent bir arayüze sahip olduğu için DbModelBuilder sınıfı gayet kolay bir şekilde ilişkilerimizi tanımlayabildik.

Code first olarak devam ettiğimiz için Sql Server üzerinde henüz hiç bir veritabanı bulunmamaktadır. EFBlogContext hazır olduğuna göre şimdi poco sınıflarımızdan yola çıkarak kolay bir şekilde veritabanı nasıl oluşturulur ona bir bakalım.

Bu işleme başlamadan önce henüz bir sunum katmanı bulunmadığı için App.config configuration tag’inin içerisinde ve configSections tag’inin altında bir conenction string tanımlayalım.

  <connectionStrings>
    <add
      name="BlogContext"
      connectionString="Data Source=localhost;Initial Catalog=BlogContext;Integrated Security=True;MultipleActiveResultSets=True;"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

Şimdi sırasıyla View->Other Windows-> Package Manager Console açıyoruz ve üzerindeki Default project kısmında RepveUOW.Data seçili olduğundan emin oluyoruz.

Açılan konsola önce: Enable-Migrations –EnableAutomaticMigrations yazıyoruz ve connection string’de herhangi bir hata yapmadı isek Migrations isminde bir klasör oluşturup içerisinde Configuration sınıfını oluşturduğunu göreceğiz.

Şimdi tekrar Package Manager Console üzerinde Update-Database yazarak veritabanını oluşturmasını sağlıyoruz.

Veritabanımız ve context’imiz oluştuğuna göre şimdi generic repository sınıfını tasarlamaya başlayabiliriz. Hemen data katmanımıza Repositories isminde bir klasör ekleyerek içerisinde IRepository arayüzünü tasarlamaya başlıyoruz.

IRepository

using System;
using System.Linq;
using System.Linq.Expressions;

namespace RepveUOW.Data.Repositories
{
    /// <summary>
    /// Model katmanımızda bulunan her T tipi için aşağıda tanımladığımız fonksiyonları gerçekleştirebilecek generic bir repository tanımlıyoruz.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IRepository<T> where T : class
    {
        IQueryable<T> GetAll();
        IQueryable<T> GetAll(Expression<Func<T, bool>> predicate); // LINQ desteği sunabilmek içinde expression'ları kullanıyoruz.
        T GetById(int id);
        T Get(Expression<Func<T, bool>> predicate);

        void Add(T entity);
        void Update(T entity);
        void Delete(T entity);
        void Delete(int id);
    }
}

Şimdi entity framework için kullanıyor olacağımız EFRepository sınıfını ekleyerek kodlarını aşağıdaki gibi yazıyoruz.,

using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;

namespace RepveUOW.Data.Repositories
{
    /// <summary>
    /// EntityFramework için hazırlıyor olduğumuz bu repositoriyi daha önceden tasarladığımız generic repositorimiz olan IRepository arayüzünü implemente ederek tasarladık.
    /// Bu şekilde tasarlamamızın ana sebebi ise veritabanına independent(bağımsız) bir durumda kalabilmek. Örneğin MongoDB için ise ilgili provider'ı aracılığı ile MongoDBRepository tasarlayabiliriz.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class EFRepository<T> : IRepository<T> where T : class
    {
        private readonly DbContext _dbContext;
        private readonly DbSet<T> _dbSet;

        public EFRepository(EFBlogContext dbContext)
        {
            if (dbContext == null)
                throw new ArgumentNullException("dbContext can not be null.");

            _dbContext = dbContext;
            _dbSet = dbContext.Set<T>();
        }

        #region IRepository Members
        public IQueryable<T> GetAll()
        {
            return _dbSet;
        }

        public IQueryable<T> GetAll(Expression<Func<T, bool>> predicate)
        {
            return _dbSet.Where(predicate);
        }

        public T GetById(int id)
        {
            return _dbSet.Find(id);
        }

        public T Get(Expression<Func<T, bool>> predicate)
        {
            return _dbSet.Where(predicate).SingleOrDefault();
        }

        public void Add(T entity)
        {
            _dbSet.Add(entity);
        }

        public void Update(T entity)
        {
            _dbSet.Attach(entity);
            _dbContext.Entry(entity).State = EntityState.Modified;
        }

        public void Delete(T entity)
        {
            // Eğer sizlerde genelde bir kayıtı silmek yerine IsDelete şeklinde bool bir flag alanı tutuyorsanız,
            // Küçük bir refleciton kodu yardımı ile bunuda otomatikleştirebiliriz.
            if (entity.GetType().GetProperty("IsDelete") != null)
            {
                T _entity = entity;

                _entity.GetType().GetProperty("IsDelete").SetValue(_entity, true);

                this.Update(_entity);
            }
            else
            {
                // Önce entity'nin state'ini kontrol etmeliyiz.
                DbEntityEntry dbEntityEntry = _dbContext.Entry(entity);

                if (dbEntityEntry.State != EntityState.Deleted)
                {
                    dbEntityEntry.State = EntityState.Deleted;
                }
                else
                {
                    _dbSet.Attach(entity);
                    _dbSet.Remove(entity);
                }
            }
        }

        public void Delete(int id)
        {
            var entity = GetById(id);
            if (entity == null) return;
            else
            {
                if (entity.GetType().GetProperty("IsDelete") != null)
                {
                    T _entity = entity;
                    _entity.GetType().GetProperty("IsDelete").SetValue(_entity, true);

                    this.Update(_entity);
                }
                else
                {
                    Delete(entity);
                }
            }
        }
        #endregion
    }
}

Repositorimiz hazır olduğuna göre artık Unit of Work desenini tasarlamaya başlayabiliriz. UnitOfWork isminde bir klasör daha oluşturarak içerisinde IUnitOfWork arayüzünü tanımlıyoruz.

using System;
using RepveUOW.Data.Repositories;

namespace RepveUOW.Data.UnitOfWork
{
    public interface IUnitOfWork : IDisposable
    {
        IRepository<T> GetRepository<T>() where T : class;
        int SaveChanges();
    }
}

İçerisinde istediğimiz tipteki repository’i bize getirecek olan GetRepository generic metotunu ve kaydetme işlemlerini toplu yapabilmemizi sağlayacak olan SaveChanges metotunun imzalarını tanımlıyoruz.

Şimdi yine entity framework için kullanacağımız EFUnitOfWork sınıfımızı UnitOfWork klasörü içerisinde tanımlıyoruz.

using System;
using RepveUOW.Data.Repositories;
using System.Data.Entity;

namespace RepveUOW.Data.UnitOfWork
{
    /// <summary>
    /// EntityFramework için oluşturmuş olduğumuz UnitOfWork.
    /// EFRepository'de olduğu gibi bu şekilde tasarlamamızın ana sebebi ise veritabanına independent(bağımsız) bir durumda kalabilmek. Örneğin MongoDB için ise ilgili provider'ı aracılığı ile MongoDBOfWork tasarlayabiliriz.
    /// </summary>
    public class EFUnitOfWork : IUnitOfWork
    {
        private readonly EFBlogContext _dbContext;

        public EFUnitOfWork(EFBlogContext dbContext)
        {
            Database.SetInitializer<EFBlogContext>(null);

            if (dbContext == null)
                throw new ArgumentNullException("dbContext can not be null.");

            _dbContext = dbContext;

            // Buradan istediğiniz gibi EntityFramework'ü konfigure edebilirsiniz.
            //_dbContext.Configuration.LazyLoadingEnabled = false;
            //_dbContext.Configuration.ValidateOnSaveEnabled = false;
            //_dbContext.Configuration.ProxyCreationEnabled = false;
        }

        #region IUnitOfWork Members
        public IRepository<T> GetRepository<T>() where T : class
        {
            return new EFRepository<T>(_dbContext);
        }

        public int SaveChanges()
        {
            try
            {
                // Transaction işlemleri burada ele alınabilir veya Identity Map kurumsal tasarım kalıbı kullanılarak
                // sadece değişen alanları güncellemeyide sağlayabiliriz.
                return _dbContext.SaveChanges();
            }
            catch
            {
                // Burada DbEntityValidationException hatalarını handle edebiliriz.
                throw;
            }
        }
        #endregion

        #region IDisposable Members
        // Burada IUnitOfWork arayüzüne implemente ettiğimiz IDisposable arayüzünün Dispose Patternini implemente ediyoruz.
        private bool disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    _dbContext.Dispose();
                }
            }

            this.disposed = true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}

Unit of Work desenide hazır olduğuna göre katmanların son görünümüne bir bakalım:

katmanlar

 

Temel bir alt yapı tasarımımızı tamamladığımıza göre kullanımlarına bakabilmek amaçlı RepveUOW.Presentation.UnitTest isminde bir Unit Test projesi oluşturalım.

RepveUOW.Presentation.UnitTest katmanını oluşturduktan sonra References kısmından Add Reference seçeneğini seçerek, Data katmanında yaptığımız gibi Projects sekmesinden Data ve Model katmanımızı referans olarak ekliyoruz.

EntityTest isminde bir UnitTest sınıfı oluşturarak içerisinde aşağıda açıklamaları ile de görebildiğiniz gibi ilgili test metotlarımızı tanımlıyoruz.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using RepveUOW.Data;
using RepveUOW.Data.UnitOfWork;
using RepveUOW.Data.Model;
using RepveUOW.Data.Repositories;

namespace RepveUOW.Presentation.UnitTest
{
    /// <summary>
    /// Repository ve UOW kullanarak ilgili test metotlarımızı yazıyoruz.
    /// </summary>
    [TestClass]
    public class EntityTest
    {
        // Entity framework için geliştirmiş olduğumuz context. Farklı ORM veya Veritabanı içinde bu context'i değiştirebiliriz.
        private EFBlogContext _dbContext;

        private IUnitOfWork _uow;
        private IRepository<User> _userRepository;
        private IRepository<Category> _categoryRepository;
        private IRepository<Article> _articleRepository;

        [TestInitialize]
        public void TestInitialize()
        {
            _dbContext = new EFBlogContext();

            // EFBlogContext'i kullanıyor olduğumuz için EFUnitOfWork'den türeterek constructor'ına
            // ilgili context'i constructor injection yöntemi ile inject ediyoruz.
            _uow = new EFUnitOfWork(_dbContext);
            _userRepository = new EFRepository<User>(_dbContext);
            _categoryRepository = new EFRepository<Category>(_dbContext);
            _articleRepository = new EFRepository<Article>(_dbContext);
        }

        [TestCleanup]
        public void TestCleanup()
        {
            _dbContext = null;
            _uow.Dispose();
        }


        [TestMethod]
        public void AddUser()
        {
            User user = new User
            {
                FirstName = "Gökhan",
                LastName = "Gökalp",
                CreatedDate = DateTime.Now,
                Email = "gok.gokalp@yahoo.com",
                Password = "123456"
            };

            _userRepository.Add(user);
            int process = _uow.SaveChanges();

            Assert.AreNotEqual(-1, process);
        }

        [TestMethod]
        public void GetUser()
        {
            User user = _userRepository.GetById(1);

            Assert.IsNotNull(user);
        }

        [TestMethod]
        public void UpdateUser()
        {
            User user = _userRepository.GetById(1);

            user.FirstName = "Mehmet";

            _userRepository.Update(user);
            int process = _uow.SaveChanges();

            Assert.AreNotEqual(-1, process);
        }

        [TestMethod]
        public void DeleteUser()
        {
            User user = _userRepository.GetById(1);

            _userRepository.Delete(user);
            int process = _uow.SaveChanges();

            Assert.AreNotEqual(-1, process);
        }
    }
}

Not: Test metotlarını çalıştırmadan önce App.config içerisinde configSections tag’inin altına tekrardan ilgili connection string’i eklemeyi unutmuyoruz.

Bu makalemizde temel bir şekilde kurumsal düzeyde alt yapı tasarlarken Generic Repository ve Unit of Work desenlerinin uygulanışını görmüş olduk.

İlgili proje örneğini aşağıdan indirebilirsiniz.

RepveUOW

 

Kategori:ArchitecturalKurumsal Tasarım Kalıpları (Enterprise Design Patterns)

107 Yorum

  1. Kaan Can Kaan Can

    Selam, makale çok açıklayıcı olmuş teşekkürler.
    Unit test haricinde web tarafında unit of work’ü kullanmak için yine her controller’ın constructor’ında mı unitofwork’e dbcontext’i tanımlayacağız?

    • Merhaba, base bir controller oluşturabilirsiniz. Base controller içerisinde bir IOC tool’u ile istediğiniz concrete UnitOfWork type’ını ve Context’i inject edebilirsiniz.

      • Kaan Can Kaan Can

        Önerebileceğiniz bir örnek var mı acaba?
        Bu bahsettiğiniz sanırım Unity ile depencity injection yapmakla aynı şey değil mi?

        • Evet Unity ile yapabilirsiniz injection işlemini. İnternet üzerinde unity örneklerini bulabilirsiniz.

  2. kerem kerem

    Merhabalar,

    Açıklayıcı bir makale. Elinize sağlık. Anlayamadığım kısım IUnitWork içinde tanımlanan GetRepository() generic metodunun ne için tanımlandığı ? Kullanım olarak bir satır ibaresi görünmüyor.

    • Merhaba, tanımlanan method aslında EFUnitOfWork sınıfını initialize ettikten sonra repository’leri kolaylıkla oluşturabilmek içindi. Örneğin:

      _userRepository = _uow.GetRepository< User >();

      gibi. Açıkcası bu örnek UI kısmına bir MVC projesi hazırlıyordum, fakat vaktim yetmediği için test case’lerinde bu şekilde kalmış. 🙂

      Teşekkürler dikkatiniz için.

  3. Hamza Cakir Hamza Cakir

    Ellerine saglik. Güzel olmus. Makalen bana bayagi yardimci oldu.

  4. Ahmet Kurt Ahmet Kurt

    Merhaba,

    Yazılarınızı yeni takip etmeye başladım. Bu faydalı konular hakkında bizleri bilgilendirdiğiniz için öncelikle çok teşekkür ediyorum.

    Şöyle bir sorum olacaktı:

    Repository içerisindeki Delete metodunda neden EntityState.Detached kullanmadığınızı öğrenebilir miyim? Sadece bilgi almak için soruyorum. EntityState.Deleted kodunu özellikle mi kullandınız?

    Bir de UnitOfWork içerisindeki Database.SetInitializer(null); kodunun neden kullanıldığını anlayamadım.

    Yanlış anlamayın lütfen sadece bilgi almak ve öğrenmek için soruyorum.

    Yeni yazılarınızı bekliyorum. 😉

    İyi çalışmalar.

    • Merhaba, öncelikle ben teşekkür ederim.
      İlk soruna gelecek olursak eğer, EntityState’in değişmesinin sebebi; Entityframework kendi içerisinde bir “change tracking” mekanizmasına sahiptir. Kısaca bahsetmem gerekirse eğer, daha önceden çekmiş olduğun entity’lerin state’lerini tutmaktadır üzerinde. Örneğin sadece bir proprety güncelledi isen, bunun update sorgusunda sadece güncellediğin property’leri göndermesi gibidir. Silinme aşamasında ise önce silinip silinmediğini kontrol edip sonrasında context’e attach’leyip siliyoruz. Bu konuyu biraz detaylı google’layabilirsin “change tracking”. Bir diğer sorun ise, SetInitializer kısmında herhangi bir Database initializer kullanmamadır null bırakmam. Orada istersen IDatabaseInitializer interface’inden implemente ettiğin bir sınıf ile initialize edebilirsin veya inbuilt olarak içerisinde bulunan, DropCreateDatabaseAlways veya DropCreateDatabaseIfModelChanges gibi sınıfları kullanabilirsin.

      İyi günler dilerim.

      • Ahmet Kurt Ahmet Kurt

        Vakit ayırıp bilgilendirdiğiniz için çok teşekkür ederim. “change tracking” konusunu araştıracağım.

  5. Ömer Can Ömer Can

    Merhaba, öncelikle makale için teşekkürler. Veri tabanını package manager console’dan Update-Database diyerek oluştururken A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 – Error Locating Server/Instance Specified) şeklinde bir hata alıyorum sebebi ne olabilir ?

    • Merhaba, Migration sırasında bu işlemi yapmadan önce, Package Manager’ın Default project kısmında RepveUOW.Data seçili olduğundan emin olun ve, içerisindeki app.config’de bir connection string’in tanımlı olduğundan emin olun.

      • Ömer Can Ömer Can

        Default olarak RepveUOW.Data seçili ve app.config’de connectionString’i tanımladım. Bildiğiniz başka bir sebebi olabilir mi ?

        • Gökhan Gökhan

          Sağ kısımda solutiondaki RepveUOW.Data class library sağ tıklayıp set as startup project seç sonra buildleyip tekrar dene olur.

  6. Nazmi Altun Nazmi Altun

    Merhaba Gökhan,

    Güzel makale. Yalnız, IUnitOfWork’de fonksiyon ismi olarak SaveChanges kullanmak, interface’i EntityFramework için yazılmış gibi gösteriyor. Commit daha uygun bir isim olabilirdi. Aynı şekilde interface’de Rollback eksik. Onun haricinde güzel makale, eline sağlık.

  7. Ahmet Kurt Ahmet Kurt

    Merhaba,

    Neden repository içerisinde iki tane Delete metodu kullandınız acaba? Sonuçta her ikisi de aynı amacı gerçekleştirmiyor mu? Çünkü Delete(int id) de Delete(T entity) metodunu çağırıyor.

    GetById metodunu test metodu içerisinden çağırsam ve entity’i elde ettikten sonra sadece Delete(T entity) metodunu kullanarak silme işlemi yapsam olmaz mı?

    Ben sadece Delete(T entity) metodunu kullanmak istiyorum ama yanlış yapmak istemiyorum. Bu yüzden müsait bir vaktinizde sizden bilgi almak istiyorum.

    Şimdiden çok teşekkürler.

    • Merhaba,
      İstediğiniz method imzasını kullanabilirsiniz Delete işlemi için, bu sadece kullanım kolaylığı için yazılmış bir method imzası. Yeri gelir elinizde sadece entity’nin Id’si olur, yeri gelir sadece entity olur gibi. Kullanımı size kalmış. Bir nevi delete işlemini ilgili developer arkadaşa, tek satırda yapabilme yeteneğini sağlamak diyebiliriz.

      • Ahmet Kurt Ahmet Kurt

        Özel bir sebebi mi var acaba diye düşünmüştüm. Cevabınız için çok teşekkür ederim.

  8. Ahmet Kurt Ahmet Kurt

    Tekrar Merhaba,

    Bu konu başlığı altındaki üçüncü sorum olacak, umarım çok rahatsız etmiyorumdur. 🙂

    EFRepository içerisinde id’ye göre exists kontrolü yapmak istiyorum. IEntity adında bir interface ürettim ve içerisinde de “Guid id { get; set; }” tanımladım. Ardından bunu EFRepository’si içerisinde şu şekilde tanımladım:

    “public class EFRepository : IRepository where T : class, IEntity”

    Sonra “public bool Exists(Guid id)” metodunu oluşturdum ve sadece aşağıdaki kodu return ettim:

    “return dbSet.Any(e => e.Id == id);”

    Projeyi build ettiğimde EFUnitOfWork sınıfımda “public IRepository GetRepository() where T : class” metodunda hata aldım. Hata da şu şekilde:

    “The type ‘T’ cannot be used as type parameter ‘T’ in the generic type or method ‘EFRepository’. There is no implicit reference conversion from ‘T’ to ‘SolutionName.Data.Repository.IEntity’.”

    Gün boyunca bu hatayı çözmeye çalıştım. Aslında tek yapmak istediğim id’ye göre exists kontrolü yapmak. Bunu dbSet.Find(id) ile de yapabilirim ama Any ile sadece var/yok bilgisini almak istiyorum. Find bana komple kaydı döndürecek. Bana performans açısından daha etkili bir şey gerekiyor. Bunun da Any olacağını düşündüm.

    Bunu en etkin şekilde nasıl yapabilirim acaba? Tavsiyeleriniz benim için çok önemli.

    Teşekkürler.

    • Halit Yurttaş Halit Yurttaş

      Where T konusunda hata alan arkadaş T nerede sınıfa geçiyor, yani T yi sınıfla özdeştirmemişsin. Class ın veya class ın interfacelerinden birinin template ine T yi geçirmen gerek. Yani class efrepo gibi

    • Üzerinden uzun zaman geçmiş ve elbette çözümü bulunmuştur ama yine de bu soruyu görenler açısından, bu cevabı da eklemek gerek bence.

      Eğer soru içerisinde yazılan kod birebir bu şekilde ise, muhtemel hata şu satırda:

      public class EFRepository : IRepository where T : class, IEntity

      Bu satırın düzeltilip şöyle yazılması gerekir:

      public class EFRepository : IRepository where T : class, IEntity

  9. kamil ocak kamil ocak

    Merhaba hocam,
    Benim iki farklı sorum olacak.

    1- Migrations kısmını enable etmek şart mıdır? Örneğin biz entityframework ile sunucuda çalışan bir veritabanı ile çalışıyorsak nasıl bir yol izlemeliyiz?

    2- UnitOfWork bize ne gibi yarar sağladı bu projede net bir şekilde göremedim. Olmasaydı ne olurdu mesela?

    • Merhabalar,

      1) Öncelikle ef ile sunucuda çalışan bir veritabanı ile çalışıyorsak kısmını tam anlayamadım. Zaten olması gereken değil mi? Buradaki migrations sorunuz sizin yaklaşımınız ile ilgili açıkcası. Code-first, model-first, db-first vb. Code-first gidiyorsanız ve entity modelinizin db deki tablonuzu tamamen prezente etmesini istiyorsanız (herhangi bir değişim karşısında) migration’ı enable etmelisiniz.
      2) UnitOfWork genelde transaction’ları yönetmekte kullanabilirsiniz. Tabi transactional bir işlem gerçekleştiriyorsanız business case’lreinizde. Bunlar dışında zaten EF kendi içerisinde bir uow barındırıyor. Faydasını bu case’de görememenizin sebebi ise belkide test case’lerini biraz daha açmalıydım. Örneğin add user örneğinde sadece user oluşturup en son _uow.SaveChanges(); diyerek ekletmek yerine, bir kaç farklı işlem daha gerçekleştirip tek bir pipe üzerinden _uow.SaveChanges(); dersek daha anlamlı olabilirdi…

      Saygılar.

  10. Gürkan Gürkan

    öncelikle yazınız için çok teşekkür ederim. işimi fazlası ile gördü. fakat karşılaştığım bir durum var. bir projede Repository Class’ı abstract olarak tanımlı. ( public abstract class Repository : Irepository where T : class )
    ben unit’Test de
    private IUnitOfWork _uow
    private Irepository _tabloAdiRepository; olarak tanımlayıp

    TestInitialize methodunda ise
    _tabloAdiRepository= new Repository(_dbContext);

    yazdığımda hata alıyorum. abstract class’lar için kullanımı hakkında bilgi verirseniz çok iyi olur. iyi günler.

    • Merhaba, ilgili repository class’ınızın bir concrete repository’si olmalıdır. Zaten _tabloAdiRepository = new Repository diyerek hangi reposiyory olduğunu dbContext üzerinden resolve edemezsiniz. Abstraction üzerinden gitmelisiniz. Saygılar.

  11. hakan hakan

    Çok güzel bir yazı olmuş.
    orcle içinde geçerli bir infrastructure olabilir mi?Değişiklik nerelerde yapılmalı?
    Teşekkürler.

    • Merhaba, ilgili oracle client’ı ile repository’leri oluşturduktan sonra neden olmasın.

  12. hakan hakan

    Cevap için Teşekkürler Gökhan Bey,
    ikinci bir sorumda şu;
    Denemeden sormak istiyorum.İstediğim mimari “db first” ,
    Model katmanını ve Migration ‘ı kaldırıp.
    Model katmanında edmx dosyamızı tutmamız yeterli olur mu?
    n tier yapıda db first mimariye sahip bir infrastructure bulamadım.
    i

    • Ne kastettiğinizi tam anlayamadım burada konu ile alakalı? Migration’ı kullanıp kullanmamak size kalmış.

  13. özgür özgür

    Merhaba,
    UnitOfwork un içierinde GetRepository tam olarak ne işe yarıyor kafama takıldı, örnekleyebilir misiniz?
    bu arada makale güzel olmuş elinize sağlık.

    • özgür özgür

      _uow = new UnitOfWork();
      repo = _uow.GetRepository();

      aslında business katmanı olarak nasıl inşa edebiliriz.

    • Merhaba, tanımlanan method aslında EFUnitOfWork sınıfını initialize ettikten sonra repository’leri kolaylıkla oluşturabilmek içindi. Örneğin:
      _userRepository = _uow.GetRepository();

  14. Rec Rec

    Elinize sağlık çok yararlı makale Tam isabet olmuş eğitim videoları çekmeyi düşündünuzmu hic. Çünkü Türkçe kaynak bulmakta çok zorlanıyoruz.

  15. Recep Recep

    Merhaba dbcontext sürekli null geliyor mvc projesinde kullanmak istediğimde acaba değerlendir vaktiniz varmı. sayğılar.

    • Merhaba, dbcontext hangi aşamada null geliyor? “EFUnitOfWork” u initialize ederken, “EFBlogContext”i constructor üzerinden inject ediyor musunuz?

  16. Recep Recep

    Merhaba class a özel olarak ekleyemedim bir türlü yada uyarlayamadım;

    IQueryable Etiketler(string[] etiketler);
    void EtiketEkle(int IlanId, string Etiket);
    void IlanEtiketEkle(int IlanId, string[] etiketler);

    public void EtiketEkle(int IlanId, string Etiket)
    {
    if (Etiket != null && Etiket != string.Empty)
    {
    string[] Etikets = Etiket.Split(‘,’);
    foreach (var tag in Etikets)
    {
    Etiket etiket = this.Get(x=> x.EtiketAdi.ToLower() == tag.ToLower().Trim());
    if (etiket == null)
    {
    etiket = new Etiket();
    etiket.EtiketAdi = tag;
    Insert(etiket);
    Save();
    }
    }
    IlanEtiketEkle(IlanId, Etikets);
    }
    }

    public void IlanEtiketEkle(int IlanId, string[] etiketler)
    {
    var ilan = _context.Ilanlar.FirstOrDefault(x => x.Id == IlanId);
    var gelenetiket = this.Etiketler(etiketler);
    ilan.Etiket.Clear();
    gelenetiket.ToList().ForEach(etiket => ilan.Etiket.Add(etiket));
    }

    public IQueryable Etiketler(string[] etiketler)
    {
    return _context.Etiketler.Where(x => etiketler.Contains(x.EtiketAdi));
    }

    çok uğraştım ama interface tamam ama repository class a uyarlayamadım.

    • Merhaba, tam olarak nasıl uyarlayamadınız? Tam olarak class’ı, interface ile atabilir misiniz?

  17. taci sufi taci sufi

    Merhabalar; Repository design patter’ni çoka çok (many to many) ilişkili alanlarda veri eklemek, silmek ve güncellemek için nasıl kullanabiliriz? Birden fazla ilişkili alanlar olduğunda bir türlü ekleyemedim. Örneğin, yukarıda yapmış olduğunuz örnekte Article tablosuna nasıl ver eklenir?

  18. Metin Metin

    Tam aradığım gibi bir kaynak oldu bu teşekkürler.

  19. makalenin kendisi inanılmaz yorumlar ve cevapları ise tadından yenmiyor.

  20. Yunus Yunus

    Merhaba bu güzel makale için teşekkür ederim.
    Birkaç sorum olacak.

    1-Kitabınızdaki örnek projede UnitOfWork kullanmamıştınız. SaveChagnes metodu RepositoryBase sınıfının içerisinde bulunuyordu. Bu konuda “Best Practices” hangisi ? (Makalenizdeki yapı mı yoksa kitaptaki yapı mı? )

    2-Yine kitabınızda her entity için ayrı bir Repository arayüzü oluşturmuştunuz. Burada ise generic parametre alan genel bir IRepository arayüzü görüyorum. Yine ilk sorudaki gibi “Best Practices” hangisi?

    3-Kitabınızdaki projeye mock objelerle unit test uygulamak istiyorum. Hangi nesneyi mocklamamı önerirsiniz Repository’leri mi yoksa Context nesnesini mi ?

    • Merhaba,

      Öncelikle teşekkür ederim yorumunuz için.

      1) Herhangi bir best practice öneremeyeceğim bu konuda. Tamamen domain modelinizin ihtiyaçlarına göre değişebilmekte kuracağınız yapı.
      2) Eğer video izlediyseniz, her entity için bir repository oluşturmamalıyız, domain modelimize göre oluşturmalıyız diye belirtmiştim. Generic olarak üretmek, implementasyonda diğer domain modeller için başka bir repository interface’i oluşturulamayacağı anlamına gelmez. IRepository interface’ini implemente eden sınıfın, farklı ihtiyaçlarına göre IFooRepository interface’i daha tanımlanabilir ve implemente edilebilir. Buda ihtiyaçlarınıza göre şekillenmektedir.
      3) Bu konuda buraya bir göz atmanızı tavsiye edebilirim. Context mock’lamak için benimde kullandığım bir kütüphanedir. https://blog.goyello.com/2016/07/14/save-time-mocking-use-your-real-entity-framework-dbcontext-in-unit-tests/

      • Yunus Yunus

        Çok teşekkür ediyorum yanıtlarınız için.

  21. Güzel ve çok faydalı bir yazı, emeğiniz için teşekkürler. Delete yaparken soft-delete’i de hesaba katmanız hoş olmuş.

  22. Faruk Faruk

    Merhaba Çok güzel bir anlatım olmuş.
    Benim 2. bir seçenek olarak DbFirst ile repository Nasıl kullanacağız. Buradaki paylaştığınız kodlara bakarak yaptım ama GetAll Tüm verileri getir dediğimde

    public List GetAll()
    {
    return olusacakTablo.ToList();
    }

    ModelValidationException hatası alıyorum birşey kaçıyorum ama ne bulamadım.
    Şimdiden teşekkürler.

    • Merhaba, aldığınız exception’daki inner exception nedir acaba? Teşekkür ederim.

  23. Ali Ali

    Merhabalar, yazınızı çok beğendim. Yalnız kalıplara uygun bir mimaride repository içinde update metodu olmaması gerekir.

    • Merhaba, teşekkür ederim öncelikle.

      Evet tam tamına kalıplara uymak isterseniz repository içerisinde “save” ve “update” olmamalıdır. Ama bir çok kompleks olmayan case de de hayat kurtarmaktadır :). Tekrardan teşekkürler güzel yorumunuz için.

  24. murat murat

    Merhaba Gökhan Bey yazı için saolun.

    Ben de orm bağımsız çalışabilecek generic bir repository tasarlamaya çalışırken yazınızı gördüm.
    Ben unit of work ile repositoryi birleştirdim ve unit of workü uçurdum çünkü bana gereksiz geldi örneğin add için
    public void Add(T entity)
    {
    _dbSet.Add(entity);
    _dbContext.SaveChanges();
    }
    şeklinde bir tabloya veri ekleyeceğimde sadece add çağrıyorum ayrıca savechanges çağırmıyorum. Bunun ileride ne sorun çıkaracağını bulamadım. Sizin bir öngörünüz var mıdır?

    2. sorum ise unit of workü dispose ettiğinizi gördüm ama repository classında dispose bulunmuyor, bunun sebebi nedir? bende hiç dispose implantationu bulunmuyor dependency injection kullanıyorsak otomatik dispose olmazmı?

    • Merhaba, açıkçası bu durum hangi proje ile çalışıyorsunuz sorusuna göre değişir. Örneğin:

      fooRepository.Add(entity);
      todoRepository.Add(entity2);

      gibi bir case karşısında, siz içeride zaten “SaveChanges” yaptığınız için 2 kere call yapmış olacaksınız. Bir başka case’de ise audit işlemlerini de tek bir yerden yönetebilmek vb. durumlar için gereklidir. Tabi dediğim gibi, projenize göre değişiklik gösterebilir. 2. sorunuzda ise, IDisposable implemente etmezseniz bir nevi kendinizi GC’ün ellerine bırakıyorsunuz. Ayrıca özel olarak dispose etmek istediğiniz nesneleriniz varsa, onları da dispose edebilirsiniz vb. gibi durumlar için implemente etmiştim o kısımda.

      Teşekkürler.

  25. Ben unit of work prensibiyle çalışırken uow’u biraz daha singleton; statick minvalinde hazırlıyor, ihtiyacım olan tüm repoları buradan static olarak elde ediyorum. Acaba doğru bir çözüm müdür?

    • Merhaba, UoW’u tam ne için kullandığınızı anlayamadım. Repository create edecek bir factory olarak mı kullanıyorsunuz?

  26. ahmet ahmet

    Gökhan merhaba, gerçekten çok iyi bir makele olmuş teşekkür ediyorum. Repository pattern için veritabanı işlemleri için kod tekrarını önlemek Generic repository için ise ilk madde ve veritabanı ve orm bağımlılığını kaldırmak için kullanıyoruz diyebilir miyiz ben öyle anladım ?

    • Merhaba, ben generic repository’e biraz daha template gözüyle bakıyorum açıkcası. En temel olarak CRUD işlemlerini ortak bir noktada toplamak gibi. Onun dışında farklı repository’ler içerisinde db yapınıza göre farklı işlemler yapmanız gerekebilecektir.

  27. nuri nuri

    Gökhan hocam, böyle bir kullanım senaryosunda cache yapısını nasıl kullanmalıyız. Repository içindeki metodlarda mı cache kontrolü yapmak gerekir? Bir örnek gösterebilir misiniz

    • Merhaba, caching gibi cross cutting’leri repository içerisinde değil de, bu repository’leri kullanacağınız servis düzeyinde cache’lemeniz daha sağlıklı olacakır.

  28. coder coder

    Singultan+factory+adaptor tasarım desenleri uygulanabilir. Facade ile tadından yenmez olur.

  29. gökhan beyaz gökhan beyaz

    Merhaba Gökhan Hocam , database first modelini kullanıyorum , GenericUoW kullanarak , birden fazla Database e nasıl bağlantı yapabilirim.Servis Katmanında tek bir UoW e DbContext geçirerek yapmalıyım.En iiyi tasarım nasıl olur bu konuda,tek database kullanmıyorum projede , gerektiğinde 2.bir database ile Joın yapmam gerekmekte Linq ile ?
    Bu Konuda bir örnek verebilir misiniz ? Baya bir makale inceledim ama hiç biri de GenericRepo kullanılmamış ?

    • Birden fazla database arasında join yapmak nasıl bir çözüm olur bilemedim. Ben olsam UoW üzerinden farklı context’ler inject edip ve sadece ilgili property’leri getirerek, elde ettiğim dataset üzerinde data aggregation işlemini gerçekleştirerek kullanırdım.

      • gökhan beyaz gökhan beyaz

        Merhaba Hocam,cevabın için tşk , takıldığım yerde orası zaten , UoW Consturctur ına , parametre oalrak generic DBContext geçirmeliyim ki , servis katmanında ve Controller da , direk UoW ile istediğim DbContext ten List imi çekebiliyem ,
        public EFUnitOfWork(EFBlogContext dbContext) olarak kullanıyosunuz burada , EFBlogContext değilde işte burda Consturctur da nasıl bir kodlama yapılmalı birden fazla Context i isteiğim yerde kullanmak içiin.

        • Eğer yine EF üzerinden ilerleyecekseniz, “EFUnitOfWork” class’ının constructor’ında “EFBlogContext” yerine “DbContext” alır halde çevirin, daha doğru olacaktır. Sonrasında ise “DbContext” üzerinden farklı Context’lerinizi tanımlayabilir, inject edebilirsiniz. EF değil ise, kendinize ait uow’ü, “IUnitOfWork” interface’ini implemente ederek oluşturmalısınız.

  30. web master web master

    Merhaba,
    Yazınız fevkalade güzel.
    Repository ve unitofWork patternlerinin bir dez-avantajı varmıdır?
    Aynı anda 10 milyon kullanıcısı olan bir yapı düşünelim cevap verebilir mi?
    Konudan bağımsız bir sorum daha olacaktı.
    En hızlı ORM Nesnesı olarak öneriniz nedir?

    • Bu sormuş olduğunuz sorunun tek bir cevabı yok kanımca. 10 milyon kullanıcıya cevap verecek olan nedir? Tek bir API mı, yoksa komple bir web-sitesi mi? Kaç ms? bla bla bla uzar gider. 🙂 Söz konusu scalability(ölçeklenebilirlik) olduğunda değerlendirilmesi gereken tek parametre, repository uow vb. kısımlar olmamalı. Tüm uygulama bileşenlerini nasıl design ettiğiniz ile tartışılabilecek bir konu. ORM olarak genelde Dapper kullanıyorum, .NET Core çatısı altında ise EFCore.

      • web master web master

        Komple bir site veya WebApi’yı kullanan Mobil Application cevap verecek. DB’ler foreign key’ler falan design edilmiş düşünelim. WebApi de Repository üzerinden işlem yapmakta. Ne yapılması gerekiyor.10 milyon kullanıcıyı bir anda kaldırabilmesi için? Araştırmalarım sonucunda “singleton design pattern” adında bir sonuca ulaştım. Ama daha bu alanda yeniyim(Acemiyim) ne yapmam gerek. Büyük projelerde Drapper’in herhangi bir kısıtlaması söz konusu olur mu?

        Teşekkürler

        • Konuya kodsal olarak bakmaktan ziyade, biraz daha büyük resme bakarak, uygulamaları nasıl scale edebilirim konusunu araştırmalısınız. Örneğin geliştirdiğiniz bir API 1000 kullanıcıya kaç ms response veriyor gibi. Bu kriterleri göz önüne alarak ister static olarak bir kaç API instance’ı ayağa kaldırır loadbalancer ile yükü dengelersiniz, istersenizde dockerize eder (gündem konuları) .net core ile linux ortamında daha performanslı bir şekilde kullanabilirsiniz. Söz konusu ölçeklemek yani scale olduğunda, tasarım desenlerinden ziyade büyük resme odaklanmak gerek ilk başta. Siz ister 5ms de cevap veren bir api yazın anlık 1000 kişiye, uygulama boyutunca ölçekleyemediğiniz sürece hep sıkıntı yaşıyacaksınız. Örneğin 5000 kişi geldiğinde veya 50.000+ vb.

  31. web master web master

    Komple bir site veya WebApi’yı kullanan Mobil Application cevap verecek. DB’ler foreign key’ler falan design edilmiş düşünelim. WebApi de Repository üzerinden işlem yapmakta. Ne yapılması gerekiyor.10 milyon kullanıcıyı bir anda kaldırabilmesi için? Araştırmalarım sonucunda “singleton design pattern” adında bir sonuca ulaştım. Ama daha bu alanda yeniyim(Acemiyim) ne yapmam gerek. Büyük projelerde Drapper’in herhangi bir kısıtlaması söz konusu olur mu?

    Teşekkürler

  32. Mert Mert

    Hocam bu yapıyı kurdukdan sonra bir fonksiyon içinde önce bir update peşinden birde insert yapmak istedigimde “an entity object cannot be referenced by multiple instances of ientitychangetracker” böyle bir hata alıyorum çok araştırdım ama çözüm bulamadım

      • Gülcan Gülcan

        Bende get yaptıktan sonra button submit te add() yapmak istediğimde aynı hatayı alıyorum. Şu an bir çözüm bulamadım. Sebebi ne olabilir? makale için teşekkürler.

      • Gülcan Gülcan

        Bende get yaptıktan sonra button submit te add() yapmak istediğimde aynı hatayı alıyorum. Şu an bir çözüm bulamadım. Sebebi ne olabilir? makale için teşekkürler. EFRepository.Add() methodunda bu hatayı alıyorum. UOW ya da IOC katmanında bir problem oldgunu düşünüyorum

        • Merhaba, repository’i initialize ettiğiniz kod kısmını gönderebilir misiniz?

          • Gülcan Gülcan

            Merhabalar,
            Sorunu çözdüm. UOW de Dispose() methodunu implemente etmemişim. Dispose() methodunu implemente ettim. Problem Çözüldü. Ancak benim size baska bir sorum olacak. Reporsitoryleri özelleştirmek istiyorum.Sonucta her repository de CRUD işlemleri olamayacak. Mecburen bir yerden sonra customize etmem gerekiyor. Sizin örnek verdiğiniz yapıda özellikle UOWde nasıl bir değişikliğe gitmem gerekiyor. Bununla ilgilibir makaleniz var mıdır ya da? İlginiz için teşekkürler

          • Merhaba, problemi çözmenize sevindim. Evet repository konusunda haklısınız, zaten bu makale başlığında da olduğu gibi generic repository implementasyonunu ele aldım ben. Customize etmekten ziyade repository pattern’ının kendisini implemente etmek istiyorsunuz. İstediğiniz şekilde bir makalem yok malesef. En hızlı çözüm olarak, kendi repository interface’lerinizi oluşturarak ve “EFRepository” yi base alarak, spesifik repository’ler türetebilirsiniz. “EFRepository” içerisindeki ortak CRUD method’larını hala kullanıyor olursunuz. “IUnitOfWork” içerisindeki “GetRepository” method’unu kullanmazsınız ve aşağıdaki gibi initialize edebilirsiniz.

            public class EntityTest
            {
            private EFBlogContext _dbContext;

            private IUnitOfWork _uow;
            private IUserRepository _userRepository;

            [TestInitialize]
            public void TestInitialize()
            {
            _dbContext = new EFBlogContext();

            _uow = new EFUnitOfWork(_dbContext);
            _userRepository = new UserRepository(_dbContext);
            }
            }

  33. Emeğinize sağlık. Harika bir yazı olmuş.

    Tek dikkatimi çeken konu şu oldu UOW kullanacaksak; repository ve ouw leri şu şekilde kullanmak yerine

    _uow = new EFUnitOfWork(_dbContext);
    _userRepository = new EFRepository(_dbContext);

    aşağıdaki gibi uow den türetmek daha doğru bir yaklaşım diye düşünüyorum. Sadece bir öneri. Yoksa sizin yaklaşımınız da doğru bir yaklaşım.

    private EFUnitOfWork uow = new EFUnitOfWork();
    ….

    ..

    public class XYZController : Controller
    {
    private EFUnitOfWork uow = new EFUnitOfWork();

    public JsonResult GetUserType()
    {
    IRepository _repo = uow.GetRepository();

    var data = _repo.Where().Select(x => new
    {
    x.id,
    x.definition
    }).ToList();
    }

    protected override void Dispose(bool disposing)
    {
    uow.Dispose();
    base.Dispose(disposing);
    }
    }

    • yanlış yazmışım.

      IRepository _repo = uow.GetRepository(); kısmı eksik; türeteceğimiz sınıfı da yollamamız gerekir. Böylece generic olmuş olur.

      IRepository _repo = uow.GetRepository();

      • Bahsettiğinizi yanlış anlamadı isem, repository create kısmından bahsediyorsunuz? Zaten uow üzerinde implemente durumda. Create ederken bu “public IRepository GetRepository()” signature’ı kullanabilirsiniz uow üzerinden. Sadece test case’i içerisinde belirtmedim ondan bahsediyorsanız eğer.

  34. Merhabalar,
    Yazınız gayet değerli, ama altındaki tüm soruları yanıtlamanız bence çok daha değerli.
    Bilginizin sadakasını veriyorsunuz. Teşekkürler.

  35. Tural Tural

    Merhabalar,
    Yazınız gayet basarili.
    Assert.AreNotEqual(-1, process); bunun manasi nedir ? ben Assert yazinca tanimiyor!

    • Tural Tural

      Birde ben bunu Form uzerinde çalişdirmak isdersem nasil olacaq yapi ?
      Mesela textboxdan bir veriyi kaydetmek isdersek eyer ?

      • Merhaba, nereden çalıştıracağınızın bir önemi yok. İlgili form içerisine sadece IUnitOfWork, ve ilgili IRepository interface’lerini inject etmeniz ve textbox’dan değeri okuduğunuz method içerisinde kullanmanız yeterli olacaktır.

    • Merhaba, “Microsoft.VisualStudio.TestTools.UnitTesting” in projenize ekli olduğundan emin olun. Bu bir MS test projesi.

  36. Yusuf Yusuf

    Merhaba Gökhan Bey
    Öncelikle yazılarınızın ve devamını diler ve paylaşımlarınız için teşekkür ederim.

    Benim şöyle bir sorum olacaktı
    Hazırlamış olduğum program farklı yerlerde birden fazla mevcut olan database ile çalışmasını istiyorum ama database degiştiğinde yeni geçtiği database ilgili tablolar yok ise hata veriyor İnternet de araştırma yaptım ama uygulaya bildiğim bir örnek yapı bulamadım size sormaya karar verdim
    İlgi ve yardımlarınız için şimdiden teşekkürler.
    İyi Çalışmalar.

    • Merhaba teşekkür ederim öncelikle. Kusura bakmayın geç cevap için yoğun bir dönemden geçiyorum. Database değiştiğinde kastınız nedir acaba? Tek bir context’iniz mi var? ORM tool’u kullanıyor musunuz? Bu gibi bilgiler de önemli cevap için.

      Teşekkürler.

  37. Uğur Uğur

    Merhaba,

    Makaleniz çok iyi.

    Biz de bu yapıyı kullanıyoruz. Şöyle bir sorunumuz var. Aynı metot içinde insert yapıp ardından get yaptığımız zaman geri dönen değerde ilişkili diğer kayıtları getirmiyor. Yani sanki context refresh olmuyor. Projeyi yeniden başlatınca insert kısmını atlatıp aynı kaydı çağırdığım zaman sorunsuz tüm ilişkileriyle beraber geliyor. Nasıl bir sorun olabilir?

  38. Merhaba, yazınız için teşekkür ederim. Benim bir sorum olacak. Connection String i webconfig yerine repository katmanında kullanmak istiyorum. Bu mümkünmü? Çünkü ben repository katmanını UI ve WebApi katmanlarında kullanmak istiyorum fakat connection string ifadesini sadece repository de tanımlayıp tek yerden kontrol etmek istiyorum. Nasıl yapabilirim. Teşekkürler.

    • Merhaba, öncelikle ben teşekkür ederim yorumunuz için. Açıkcası sorunuzu tam anlayamadım. Connection string’i direkt olarak repository sınıfının içerisine mi gömmekten bahsediyorsunuz? UI ve WebApi’da tanımlamak istemiyorsanız, direkt olarak repository içerisine gömmeniz gerekmektedir fakat buda ne derece doğru bir yaklaşım olur bilemedim. Sonuç olarak UI ve WebApi iki farklı application. Yönetim işlemini eğer herhangi bir deployment otomasyon tool’u varsa (Jenkins, vs.) config transform ile de tek bir noktadan da yönetebilirsiniz deployment anlarında.

  39. can can

    Merhaba makale için cok tesekkürler faydalı oldu 🙂
    Ancak bir problem ile karşılaştım.Kendıme bir proje şablonu hazırlıyorum ve sizin makelenizdeki yapıyı Data katmanıma entegre ettim.Birde IOC container olarak Autofac uyguladım.Ondan kaynaklımı bilemiyorum ancak save işlemi calısırken update ve delete işlemleri calısmıyor.
    Autofac start kodum
    var builder = new ContainerBuilder();

    builder.RegisterControllers(typeof(MvcApplication).Assembly);
    builder.RegisterType<GenericRepository>().As<IGenericRepository>();
    builder.RegisterType();
    builder.RegisterType().As();

    var container = builder.Build();
    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

    -Controller daki consructor ve delete kodum
    private IUnitOfWork _uow;
    IGenericRepository _userRepository;
    public HomeController(IGenericRepository userRepository, IUnitOfWork uow)
    {
    _uow = uow;
    _userRepository = userRepository;
    }
    public ActionResult Index()
    {
    var users = _userRepository.GetById(3);
    _userRepository.Delete(users);
    _uow.SaveChanges();

    return View();
    }
    Yardımcı olabiirseniz cok mutlu olurum.Sonuna kadar geldim burda tıkandım:)
    Eğer anlasılmıyorsa mailinize kodları da atabilirim.Şablon proje sadece zaten.
    Teşekkürler

  40. can can

    Düzeltme:Autofac start kodum u eksik yapıştırmışım.Bunu baz alabilirsiniz:)
    var builder = new ContainerBuilder();
    //Bu kısım özelleştirilebiliyor.Sadece x controllere baksın istiyorsan ‘builder.RegisterType();’ yazmalısın
    builder.RegisterControllers(typeof(MvcApplication).Assembly);
    builder.RegisterType<GenericRepository>().As<IGenericRepository>();
    builder.RegisterType();
    builder.RegisterType().As();

    var container = builder.Build();
    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

    • Merhaba, kusura bakmayın geç cevap için, tempolu bir dönemdeyim hem düğün arefeleri hem de yeni iş değişikliği. Problem hala devam ediyor mu?

      • can can

        Evet sizden haber beklıyordum bende:)

  41. Tuğba Tuğba

    Faydalı makale olmasının yanında yorumlar ve yanıtlar çok keyifli ve özenli, sırf teşekkür etmek için yorum bırakıyorum, henüz bloğunuzun tamamını incelemedim, IoC ve cross cutting yapılarla birleşince epey güzel olacak. İyi çalışmalar…

  42. Tahsin Tahsin

    Merhaba,

    EFUnitOfWork içerinde IDisposable için gerekli pattern uygulanıyor. Fakat burada Dispose() yordamı sadece TestCleanup() içerisinde kullanılmış. Gerçek hayat senaryosunda Dispose() yordamını nerede çağırmalıyız. En doğru kullanım yeri neresidir. SaveChanges() sonrası mı çağırmalıyız. Yoksa kendisi otomatik olarak mı tetikleniyor.

    Birde _userRepository = _uow.GetRepository(); kullanımı ile gerekli repository’ler dinamik bir şekilde yaratılabiliyor. Fakat bizim repository’lerimiz aşağıdaki gibi olsaydı uow deseni nasıl olmalıydı.

    Örneğin;

    ProductRepository: EFRepository
    {
    //özel operasyonlar
    }

    CategoryRepository: EFRepository
    {
    //özel operasyonlar
    }

    Son olarak amacımız zaten SaveChanges() sonrasında dispose etmek ise disposed ve disposing değerlerine gerek var mı? Zaten EFUnitOfWork ile işimiz bittikten sonra _dbContext.Dispose(); ve GC.SuppressFinalize(this); kısımlarının çalışmasını istemiyor muyuz?

  43. Fatih Fatih

    Gerçekten çok teşekkür ederim. IoC ile de makale yayınlarsanız harika olur

  44. turgay turgay

    Gökhan hocam selamlar, bu ve diğer makaleleriniz için çok teşekkür ederim.
    Ben de kullanmaya çalıştım fakat bir türlü başarılı olamadım,
    müsait olduğunuzda yardım edebilirseniz çok sevinirim 🙂 (Ben Acemi Balık :)))

    Bu kodları çok çok basit bir Form uygulamasında bir butonun altında, şu şekilde denemek istedim.
    ————————————————————————-
    public partial class Form1 : Form
    {
    public EFBlogContext _dbContext;
    public IUnitOfWork _uow;
    public IRepository _userRepository;
    public IRepository _categoryRepository;
    public IRepository _articleRepository;
    public Form1()
    {
    InitializeComponent();
    _dbContext = new EFBlogContext();
    _uow = new EFUnitOfWork(_dbContext);
    _userRepository = new EFRepository(_dbContext);
    _categoryRepository = new EFRepository(_dbContext);
    _articleRepository = new EFRepository(_dbContext);
    }

    private void btnYaz_Click(object sender, EventArgs e)
    {
    User user = new User
    {
    FirstName = “Denemeİsim”,
    LastName = “DenemeSoyisim”,
    CreatedDate = DateTime.Now,
    Email = “deneme@deneme.com.tr”,
    Password = “12344321”
    };
    _userRepository.Add(user);
    int process = _uow.SaveChanges();

    }
    }
    ———————————————————————————————

    Lakin Şu hatayı aldım.
    nerede ya da nerelerde hata yapıyorum hocam.
    System.InvalidOperationException: The entity type User is not part of the model for the current context.
    konum: System.Data.Entity.Internal.InternalContext.UpdateEntitySetMappingsForType(Type entityType)
    konum: System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)

    Saygılarımla,
    İyi Çalışmalar.

    • turgay turgay

      Hocam hallettim teşekkürler :))

  45. Hasan Mirzaoglu Hasan Mirzaoglu

    Gayet açıklayıcı bir makale olmuş, emeğinize sağlık.

  46. Cihan KÜSMEZ Cihan KÜSMEZ

    Merhaba,

    Repository ve UnitOfWork araştırmamda en etkili makale olduğunu söyleyebilirim teşekkürler. Proje üzerinde 3-4 saattir çalışıyorum. Unit Test üzerine bir ara araştırma yapmayı düşünüyordum. Sağolun siz bonus gibi onu da koymuşsunuz. Ama o kısım biraz yarım kalmış gibi sanki. Ya da sonuç alamadığım için ben öyle düşündüm.

    Yapılandırma sistemi başlatılamadı diye bir hata alıyorum. Sebebi nedir acaba ? Data.Model kısmındaki appConfig dosyasını test dosyasının altına da kopyaladım. Ama hata sebebini anlayamadım.

    ——————–
    AddUser Failed

    Yapılandırma sistemi başlatılamadı
    konum: System.Configuration.ClientConfigurationSystem.EnsureInit(String configKey)
    konum: System.Configuration.ClientConfigurationSystem.PrepareClientConfigSystem(String sectionName)
    konum: System.Configuration.ClientConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String sectionName)
    konum: System.Configuration.ConfigurationManager.GetSection(String sectionName)
    konum: System.Configuration.PrivilegedConfigurationManager.GetSection(String sectionName)
    konum: System.Diagnostics.DiagnosticsConfiguration.GetConfigSection()
    konum: System.Diagnostics.DiagnosticsConfiguration.Initialize()
    konum: System.Diagnostics.DiagnosticsConfiguration.get_IndentSize()
    konum: System.Diagnostics.TraceInternal.InitializeSettings()
    konum: System.Diagnostics.TraceInternal.get_Listeners()
    konum: System.Diagnostics.Trace.get_Listeners()
    konum: Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TraceListenerManager.Add(ITraceListener traceListener)

    • Merhaba teşekkür ederim, evet Unit Test kısımları eksik maalesef. Şuanda internetin yok bir kaç haftadır mobile’den yazıyorum. En kısa sürede kontrol edip, tekrar yorum yazacağım.

  47. Gokhan Gokhan

    Uzun zaman önceymiş konu . Burada bi repository deki custom metotun çözümü yok sanırım.
    Mesela IUserRepository.InsertCustomUser() diye bir metotum olsa generic repository işimizi görmeyecek gibi. Yukarıda yorumlarda göremedim ama bilgi amaçlı yazmak istedim.

    • Gokhan Gokhan

      Ayrıca güzel makale emeğiniz için teşekkürler.

    • Merhaba evet, custom ihtiyaçlar için kendi repository interface’lerinizi oluşturarak extend etmelisiniz. Yorumunuz için teşekkür ederim.

  48. İlker İlker

    Anlatımlar gerçekten açıklayıcı. İnternette verilen örneklerin %99u tek Dbcontext E göre yapılmış. diyelim ki iki farklı dbcontext var. Acontext ve BContext içindeki tablolarda farklı. sunucularda farklı. Acontextin a tablosuna yeni kayıt ekleyeceğim. başarılı olursa B contexin b tablosunda da güncelleme işlemi yapacağım. b tablosu güncellenirken sorun olursa Acontextin a tablosuna eklenen kaydı geri alsın.. iki işlemde başarılı olursa değişiklikleri veri tabanına uygulasın.

İlhan için bir yanıt yazın Yanıtı iptal et

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Bu site, istenmeyenleri azaltmak için Akismet kullanıyor. Yorum verilerinizin nasıl işlendiği hakkında daha fazla bilgi edinin.