Merhaba bu makalemde herhangi bir database’den bağımsız tüm database tipleri için ortak kullanılabilen IDbConnection tipinde bir connection’ı kullandığımız database’e göre nasıl üretebiliriz onu anlatmak istiyorum.
Öncelikle bunun için bir factory interface’e ihtiyacımız var.
public interface IConnectionFactory : IDisposable { IDbConnection Connection { get; } }
Burada bu interface’den türetilecek class’ların bize bir IDbConnection dönmesini zorunlu kılıyoruz ve oluşacak class’ımızda Dispose() methodu da zorunlu olacak.
Şimdi yine database’den bağımsız database bilgisi verildiğinde bize bir IDbConnection dönebilen bir factory base yaratalım. Ama bu class’ımız öyle bir class olsun ki Connection yaratsın fakat bu connection’ın hangi database’e ait olduğu bilgisi alt class’lar tarafından verilsin. Burada database bilgisini getiren bir method yapıp bu method’u soyutlaştırmalıyız.
public abstract class ConnectionFactoryBase : IConnectionFactory { private string connectionString; private bool disposedValue = false; // To detect redundant calls protected ConnectionFactoryBase(string connectionString) { this.connectionString = connectionString; } public IDbConnection Connection { get { var factory = this.GetProviderFactory(); var connection = factory.CreateConnection(); if (connection == null) { throw new Exception("There is available connection factory!"); } connection.ConnectionString = this.GetConnectionString(); if (connection.State == ConnectionState.Closed) { connection.Open(); } return connection; } } public void Dispose() { // Do not change this code. Put cleanup code in Dispose(bool disposing) above. this.Dispose(true); // GC.SuppressFinalize(this); } protected abstract DbProviderFactory GetProviderFactory(); protected virtual string GetConnectionString() { return this.connectionString; } protected virtual void Dispose(bool disposing) { if (this.disposedValue) { return; } if (disposing) { this.Connection.Close(); } this.disposedValue = true; } // ~ConnectionFactory() { // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. // Dispose(false); // } // This code added to correctly implement the disposable pattern. }
Burada kullandığımız
protected abstract DbProviderFactory GetProviderFactory();
methodu’ı bir abstract method’dur ve hangi provider’ın çağırılacağı alt class’lara bırakılmıştır.
Örneğin buradaki base class’ı kullanarak postgresql için bir connection class’ı tasarlamak istediğimizde yapacağımız işlem şu şekildedir.
public class PostgreConnectionFactory : ConnectionFactoryBase { public PostgreConnectionFactory(string connectionString) : base(connectionString) { } protected override DbProviderFactory GetProviderFactory() { var factory = DbProviderFactories.GetDbProviderFactory(DataAccessProviderTypes.PostgreSql); return factory; } }
Sadece abstract olarak tasarlanan GetProviderFactory method’umuzun içerisini postgresql’e göre tasarlayarak sorunumuzu çözmüş olduk.
Şimdi tek eksiğimiz;
DbProviderFactories.GetDbProviderFactory(DataAccessProviderTypes.PostgreSql); yapısının nasıl çalıştığını anlamak kaldı.
public enum DataAccessProviderTypes { SqlServer, SqLite, MySql, PostgreSql, } public class DbProviderFactories { public static DbProviderFactory GetDbProviderFactory(DataAccessProviderTypes type) { switch (type) { case DataAccessProviderTypes.SqlServer: return SqlClientFactory.Instance; case DataAccessProviderTypes.SqLite: return GetDbProviderFactory("Microsoft.Data.Sqlite.SqliteFactory", "Microsoft.Data.Sqlite"); case DataAccessProviderTypes.MySql: return GetDbProviderFactory("MySql.Data.MySqlClient.MySqlClientFactory", "MySql.Data"); case DataAccessProviderTypes.PostgreSql: return GetDbProviderFactory("Npgsql.NpgsqlFactory", "Npgsql"); default: throw new NotSupportedException("Unsupported_Provider"); } } public static DbProviderFactory GetDbProviderFactory(string providerName) { var providername = providerName.ToLower(); if (providerName == "system.data.sqlclient") { return GetDbProviderFactory(DataAccessProviderTypes.SqlServer); } if (providerName == "system.data.sqlite" || providerName == "microsoft.data.sqlite") { return GetDbProviderFactory(DataAccessProviderTypes.SqLite); } if (providerName == "mysql.data.mysqlclient" || providername == "mysql.data") { return GetDbProviderFactory(DataAccessProviderTypes.MySql); } if (providerName == "npgsql") { return GetDbProviderFactory(DataAccessProviderTypes.PostgreSql); } throw new NotSupportedException(string.Format("Unsupported_Provider", providerName)); } private static DbProviderFactory GetDbProviderFactory(string dbProviderFactoryTypeName, string assemblyName) { var instance = ReflectionUtils.GetStaticProperty(dbProviderFactoryTypeName, "Instance"); if (instance == null) { var a = ReflectionUtils.LoadAssembly(assemblyName); if (a != null) { instance = ReflectionUtils.GetStaticProperty(dbProviderFactoryTypeName, "Instance"); } } if (instance == null) { throw new InvalidOperationException(string.Format("UnableToRetrieveDbProviderFactoryForm", dbProviderFactoryTypeName)); } return instance as DbProviderFactory; } }
Burada assembly üzerinden Type bilgisi gönderilen database provider’ını dönen bir provider factory yazmış olduk burada da.
Artık Connection Factory yapımız hazır. Bir sonra ki makalemde görüşmek üzere.
Hoşçakalın.
Bu konuyla ilgili sorularınızı alt kısımda bulunan yorumlar alanını kullanarak sorabilirsiniz.
Referanslar
TAGs : C# SQL, Design Patterns, factory method tasarim deseni
Beynine sağlık Fatih, çok iyi.
Tebrikler. Eline emeğine sağlık harika bir paylaşım olmuş.
Junior seviyesindeki meslektaşlarımız için muazzam bir makale olmuş. Fatih gibi insanlar bu ülke için değer, umarım kıymetleri bilinir. Çünkü bildiğini paylaşma fırsatı olacak kadar zaman maliyeti yapabilen yazılımcı bir elin parmağını geçmeyecek kadar az.