Dapper Repository Kullanımı ve Örnek Proje – 3

Şimdi de gelin başlangıç tabloları ve verileri ile veri tabanımızı ve Data katmanımızın içeriğini oluşturalım. Bu aşamada öncelikle veri tabanında ne gibi işlemler yaptığımı kısaca anlatıyor olacağım. Ancak projenin en alt kısmında Github hesabıma ulaşan bir link göreceksiniz. O bağlantıya tıklayarak projeyi GitHub üzerinden çekebilirsiniz. Ayrıca projenin ana dizininde en güncel veri tabanı SQL koduna da görebilirsiniz. Şimdi veri tabanımıza geçelim.

Öncelikle veri tabanımızda şimdilik iki adet tablo kullanacağız.

Bunlardan birincisi kullanıcıları tutacağımız “Users” tablosu.

İkincisi ise, giriş denemelerini kayıt altına alacağımız “UsersLoginAttemption” tablomuz. Bu tablo ile başarılı ve başarısız tüm girişleri ve giriş denemelerini kayıt altına alıyor olacağız. Böylece kullanıcıyı bir kaç adetten fazla hatalı giriş denemesi sonunda belli bir süre bloklayabilir veya kendimize bir uyarı gönderilmesini sağlayabiliriz. Ayrıca tabii ki hangi kullanıcının ne zaman giriş yaptığını da bu yol ile belirleyebiliriz.

Gördüğünüz gibi burada “IsSuccess” alanında kullanıcının giriş denemesi başarılı mı değil mi bilgisini de tutuyor olacağız.

Şimdi veri tabanımız bu şekilde diyelim ve gelelim repository kısmını hazırlamaya. Öncelikle aşağıdaki gibi Data katmanımız altında bir adet klasör açarak işlemimize başlıyoruz.

Daha sonra hemen ardından “DapperRepository” dizinine ve “Ana Dizine” aşağıdaki görselde olduğu gibi “DapperRepository.cs” sınıfımızı ve “IRepository” sınıfını ekliyoruz.

İşte bu şekilde Repository sınıflarımızı yerine oturtmuş oluyoruz. Böylece artık içlerini kodlamaya geçebiliriz.

DapperRepository.cs Sınıfımızın İçerisi

Aşağıdaki kod bloğunu olduğu gibi kullanabiliriz;

public class DapperRepository<T> : IRepository<T> where T : BaseEntity
    {
        string _tableName;

        private SqlConnection SqlConnection()
        {
            IConfiguration configuration = new ConfigurationBuilder()
           .AddJsonFile("appsettings.json", true, true)
           .Build();

            string connectionString = configuration.GetSection("CustomApplicationSettings").GetSection("connectionString").Value;

            return new SqlConnection(connectionString);
        }

        private IDbConnection CreateConnection()
        {
            var conn = SqlConnection();
            conn.Open();
            return conn;
        }

        private IEnumerable<PropertyInfo> GetProperties => typeof(T).GetProperties();

        public int Insert(T t)
        {
            _tableName = typeof(T).Name;

            var insertQuery = GenerateInsertQuery_ScopeIdentity();

            using (var connection = CreateConnection())
            {
                return connection.ExecuteScalar<int>(insertQuery, t);
            }
        }

        public int InsertRange(IEnumerable<T> list)
        {
            _tableName = typeof(T).Name;

            var inserted = 0;
            var query = GenerateInsertQuery();
            using (var connection = CreateConnection())
            {
                inserted += connection.Execute(query, list);
            }

            return inserted;
        }

        public int Update(T t)
        {
            _tableName = typeof(T).Name;

            var updateQuery = GenerateUpdateQuery();

            using (var connection = CreateConnection())
            {
                return connection.Execute(updateQuery, t);
            }
        }

        public int Delete(int id, bool? realDelete = null)
        {
            _tableName = typeof(T).Name;

            using (var connection = CreateConnection())
            {
                if (realDelete.HasValue && realDelete.Value)
                    return connection.Execute($"DELETE FROM {_tableName} WHERE id = @id", new { id = id });
                else
                    return connection.Execute($"UPDATE {_tableName} SET silindi = 1 WHERE id = @id", new { id = id });
            }
        }

        public T Get(int id)
        {
            _tableName = typeof(T).Name;

            using (var connection = CreateConnection())
            {
                var result = connection.QuerySingleOrDefault<T>($"SELECT * FROM {_tableName} WHERE [email protected]", new { id = id });
                if (result == null)
                    return null;

                return result;
            }
        }

        public IEnumerable<T> GetList(Expression<Func<T, bool>> filter = null)
        {
            _tableName = typeof(T).Name;

            using (var connection = CreateConnection())
            {
                DommelMapper.SetTableNameResolver(new CustomTableNameResolver());

                return filter == null
                    ? connection.Query<T>($"SELECT * FROM {_tableName}")
                    : connection.Select(filter);
            }
        }

        public long GetList_Count(Expression<Func<T, bool>> filter = null)
        {
            _tableName = typeof(T).Name;

            using (var connection = CreateConnection())
            {
                DommelMapper.SetTableNameResolver(new CustomTableNameResolver());

                return filter == null
                    ? connection.QueryFirstOrDefault<long>($"SELECT COUNT(1) FROM {_tableName}")
                    : connection.Count<T>(filter);
            }
        }

        public IEnumerable<T> GetAllIds(string fieldName, List<int> Id)
        {
            _tableName = typeof(T).Name;

            string ids = string.Empty;
            foreach (var id in Id)
                ids += string.Format("{0},", id);

            string query = string.Format("SELECT * FROM {0} WHERE {1} IN ({2})", _tableName, fieldName, ids.TrimEnd(','));

            using (var connection = CreateConnection())
            {
                return connection.Query<T>(query);
            }
        }

        public IEnumerable<T> GetCustomQuery(string customQuery, DynamicParameters parameters, CommandType commandType)
        {
            using (var connection = CreateConnection())
            {
                return connection.Query<T>(customQuery, parameters, commandType: commandType);
            }
        }

        public string GetCustomQueryToString(string customQuery, DynamicParameters parameters, CommandType commandType)
        {
            using (var connection = CreateConnection())
            {
                return connection.ExecuteScalar<string>(customQuery, parameters, commandType: commandType);
            }
        }

        private static List<string> GenerateListOfProperties(IEnumerable<PropertyInfo> listOfProperties)
        {
            return (from prop in listOfProperties
                    let attributes = prop.GetCustomAttributes(typeof(DescriptionAttribute), false)
                    where (attributes.Length <= 0 || (attributes[0] as DescriptionAttribute)?.Description != "ignore") && prop.Name != "id"
                    select prop.Name).ToList();
        }

        private string GenerateInsertQuery()
        {
            var insertQuery = new StringBuilder($"INSERT INTO {_tableName} ");

            insertQuery.Append("(");

            var properties = GenerateListOfProperties(GetProperties);
            properties.ForEach(prop => { insertQuery.Append($"[{prop}],"); });

            insertQuery
                .Remove(insertQuery.Length - 1, 1)
                .Append(") VALUES (");

            properties.ForEach(prop => { insertQuery.Append($"@{prop},"); });

            insertQuery
                .Remove(insertQuery.Length - 1, 1)
                .Append(")");

            return insertQuery.ToString();
        }

        private string GenerateInsertQuery_ScopeIdentity()
        {
            var insertQuery = new StringBuilder($"INSERT INTO {_tableName} ");

            insertQuery.Append("(");

            var properties = GenerateListOfProperties(GetProperties);
            properties.ForEach(prop => { insertQuery.Append($"[{prop}],"); });

            insertQuery
                .Remove(insertQuery.Length - 1, 1)
                .Append(") VALUES (");

            properties.ForEach(prop => { insertQuery.Append($"@{prop},"); });

            insertQuery
                .Remove(insertQuery.Length - 1, 1)
                .Append(")");

            insertQuery = new StringBuilder(string.Format("{0}; SELECT SCOPE_IDENTITY();", insertQuery, _tableName));

            return insertQuery.ToString();
        }

        private string GenerateUpdateQuery()
        {
            var updateQuery = new StringBuilder($"UPDATE {_tableName} SET ");
            var properties = GenerateListOfProperties(GetProperties);

            properties.ForEach(property =>
            {
                if (!property.Equals("id"))
                {
                    updateQuery.Append($"{property}[email protected]{property},");
                }
            });

            updateQuery.Remove(updateQuery.Length - 1, 1);
            updateQuery.Append(" WHERE [email protected]");

            return updateQuery.ToString();
        }

        public class CustomTableNameResolver : DommelMapper.ITableNameResolver
        {
            public string ResolveTableName(Type type)
            {
                // Every table has prefix 'tbl'.
                return $"{type.Name}";
            }
        }
    }

Gördüğünüz gibi bir çok alanda hata veriyor. Bunun sebebi ise projemize henüz Dapper sınıfını tanıyacak paketleri yüklemedik. Şimdi gelin isterseniz projemizin .Data katmanında “Dependencies”e sağ tıkladıktan sonra “Manage NuGet Packages” seçeneğine tıklayalım. Açılan alandan ise aşağıdaki belirttiğim sınıfları ekleyelim.

  • Dapper
  • Dapper.Contrib
  • Dapper.FluentMap
  • DapperExtensions
  • Dommel (1.11.0)

Özellikle “Dommel” kütüphanesi için “v1.11.0” ı seçmemiz, projemizin doğru çalışması açısından çok ama çok önemlidir.

Ayrıca tabii ki .Data katmanımız bir DLL projesi olduğu için default olarak MVC kütüphaneleri de yüklü gelmeyecek. Bu yüzden aşağıdaki kütüphaneleri de yine “NuGet” üzerinden yüklemekte fayda olacaktır.

  • System.Data.SqlClient
  • Microsoft.Extensions.Configuration
  • Microsoft.Extensions.Configuration.Json

Bu kütüphaneleri de yükledikten ve “using” olarak ekledikten sonra sayfamızın en üstünde yer alan “using” kısmı aşağıdaki son halini almış olmalıdır.

using Dapper;
using Dommel;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

Tüm bunlar bittikten sonra geriye tek bir hata kalacaktır. Bu da “BaseEntity” hatası. Çünkü projemiz bu sınıfı göremiyor. Aslında haklı da; Çünkü daha oluşturmadık. 🙂

Şimdi gelelim BaseEntity sınıfımıza. Aslında ben, veri tabanındaki tablolarımın tümünde kullandığım alanları belirtmek için bu sınıfı kullanıyorum. Bendeki içeriği basit. Aşağıdaki gibi;

namespace DenemeProjemiz.Core
{
    public partial class BaseEntity
    {
        public int Id { get; set; }
    }
}

Ancak “namespace DenemeProjemiz.Core” kısmından da anlayacağınız gibi bu sınıfı tüm ana sınıflarımızın barındırılacağını bir önceki derste anlattığım .Core sınıfı içerisinde oluşturuyoruz.

Hemen ardından da .Data katmanımızın “Dependencies” kısmına sağ tıkladıktan sonra “Add Project Reference” dedikten sonra açılan ekrandan “DenemeProjemiz.Core” seçiyoruz ve “OK” diyoruz.

Böylece artık sınıfımız eklenmiş olacaktır. Hemen ardından da “DapperRepository.cs” sayfamızın içerisinde “using” kısmına aşağıdaki alanı eklediğimizde bütün hatalar kalkmış olacak.

using DenemeProjemiz.Core;

Artık DapperRepository.cs dosyamız kullanıma hazır. Ancak bunu bir interface arkasına aldığımızı unutmayalım! Bu yüzden şimdi interface sayfamızı (IRepository.cs) aşağıdaki şekilde güncelliyoruz.

using Dapper;
using DenemeProjemiz.Core;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq.Expressions;

namespace DenemeProjemiz.Data
{
    public interface IRepository<T> where T : BaseEntity
    {
        int Insert(T entity);
        int InsertRange(IEnumerable<T> list);
        int Update(T entity);
        int Delete(int id, bool? realDelete = null);
        T Get(int id);
        IEnumerable<T> GetList(Expression<Func<T, bool>> filter = null);
        IEnumerable<T> GetCustomQuery(string customQuery, DynamicParameters parameters, CommandType commandType);
        IEnumerable<T> GetAllIds(string fieldName, List<int> Id);
        long GetList_Count(Expression<Func<T, bool>> filter = null);
        string GetCustomQueryToString(string customQuery, DynamicParameters parameters, CommandType commandType);
    }
}

Bu işlemi de yaptıktan sonra bu dersin sonuna çok yaklaştık. Son bir hareket ile “Dependency Injection” tanımlamasını yaparak, projemiz ayağa kalktığı anda IRepository interface’i ile DapperRepository sınıfının da buluşarak ağaya kalkmalarını ve hazır hale gelmelerini sağlamamız gerekiyor. Bunun için de .Web katmanımız içerisinde yer alan “Startup.cs” dosyasını açarak “ConfigureServices” fonksiyonu içerisinde alt kısma aşağıdaki satırı ekliyoruz.

services.AddSingleton(typeof(IRepository<>), typeof(DapperRepository<>));

Gördüğünüz gibi projemiz “IRepository ve DapperRepository”yi tanımıyorum diyor. Tabii ki bu sınıflar .Data katmanında kaldığı için yine bu sefer .Web katmanımızda “Dependencies”e sağ tıklayıp “Add Project Reference” diyoruz. Hemen ardından aşağıdaki gibi üç katmanı da seçip “OK” diyoruz. Çünkü sonuçta bu Web katmanı bizim nihai katmanımız olduğu için üç katmanı da kullanacak. Sonradan tekrar uğraşmayalım değil mi? 🙂

Şimdide en son olarak “using” kısmına aşağıdaki iki satırı ekleyelim ve böylece Data katmanımızın ana işlevi de tamamlanmış olacak.

using DenemeProjemiz.Data;
using DenemeProjemiz.Data.DapperRepository;

Bu derste de bu kadar arkadaşlar. Böylece Dapper repository için gerçek çalışan bir örneğe de girişmiş olduk. Lütfen sorularınız olur ise aşağıdaki “Yorumlar” kısmından sormaya çekinmeyin.

Herkese iyi çalışmalar…

 

 

Proje Serisine Ait Diğer Sayfa Bağlantıları;

Dapper Repository Kullanımı ve Örnek Proje – 1
Dapper Repository Kullanımı ve Örnek Proje – 2
Dapper Repository Kullanımı ve Örnek Proje – 3

 

 

Proje Visual Studio 2019 ile kodlanmıştır.
Projenin son halini buraya tıklayarak github üzerinden indirebilirsiniz…

0 0 vote
Article Rating
Subscribe
Bildir
guest
0 Yorum
Inline Feedbacks
View all comments