Merhaba sevgili arkadaşlar, uzun bir zaman sonra tekrar aranızdayım. Hoşunuza gideceğiniz bir konu ile aranızda tekrar bulunmaktan dolayı mutluluk duymaktayım. .Net Core projelerinde Lazy Loading entegresinin kapalı olduğu ortamlarda ilişkili tablo verilerini getirmek için bir extension metoda ihtiyaç duyabiliriz.
Genişletilebilir bir metot yazarak .Net Core projesinde bunu çok güzel bir şekilde sağlayabiliriz ve bu yazdığımız metodu bir repository vasıtasıyla profesyonel bir şekilde kullanabiliriz. Örnek klasörleme sistemini aşağıda görebilirsiniz.

1.Adım IIncludable Interfacesini Oluşturma
Öncelikle Includable extensions implementasyonu için bir interface oluşturmamız gerekmektir. IIncludable adlı interfacemizin kodları aşağıdaki gibidir.
using Microsoft.EntityFrameworkCore.Query;
using System;
using System.Linq;
namespace FriendFinder.Repository.Generic
{
public interface IIncludable { }
public interface IIncludable<out TEntity> : IIncludable { }
public interface IIncludable<out TEntity, out TProperty> : IIncludable<TEntity> { }
internal class Includable<TEntity> : IIncludable<TEntity> where TEntity : class
{
internal IQueryable<TEntity> Input { get; }
internal Includable(IQueryable<TEntity> queryable)
{
Input = queryable ?? throw new ArgumentNullException(nameof(queryable));
}
}
internal class Includable<TEntity, TProperty> :
Includable<TEntity>, IIncludable<TEntity, TProperty>
where TEntity : class
{
internal IIncludableQueryable<TEntity, TProperty> IncludableInput { get; }
internal Includable(IIncludableQueryable<TEntity, TProperty> queryable) :
base(queryable)
{
IncludableInput = queryable;
}
}
}
2.Adım IncludableExtensions Sınıfımızı Oluşturalım
1.Adımımızı başarıyla tamamladığımızı kabul edersek şimdi extensionlarımızı yazmamız gerekmektedir. IncludableExtensions adlı statik sınıfımız aşağıdaki gibidir. Extensionlar bildiğiniz gibi ayrıca birer statik sınıflardır. NOT: 1.adımdaki yazdığınız interface’i referans olarak belirtmeyi unutmayınız.
using FriendFinder.Repository.Generic;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace FriendFinder.Repository.Extensions
{
public static class IncludableExtensions
{
public static IIncludable<TEntity, TProperty> Include<TEntity, TProperty>(
this IIncludable<TEntity> includes,
Expression<Func<TEntity, TProperty>> propertySelector)
where TEntity : class
{
var result = ((Includable<TEntity>)includes).Input
.Include(propertySelector);
return new Includable<TEntity, TProperty>(result);
}
public static IIncludable<TEntity, TOtherProperty>
ThenInclude<TEntity, TOtherProperty, TProperty>(
this IIncludable<TEntity, TProperty> includes,
Expression<Func<TProperty, TOtherProperty>> propertySelector)
where TEntity : class
{
var result = ((Includable<TEntity, TProperty>)includes)
.IncludableInput.ThenInclude(propertySelector);
return new Includable<TEntity, TOtherProperty>(result);
}
public static IIncludable<TEntity, TOtherProperty>
ThenInclude<TEntity, TOtherProperty, TProperty>(
this IIncludable<TEntity, IEnumerable<TProperty>> includes,
Expression<Func<TProperty, TOtherProperty>> propertySelector)
where TEntity : class
{
var result = ((Includable<TEntity, IEnumerable<TProperty>>)includes)
.IncludableInput.ThenInclude(propertySelector);
return new Includable<TEntity, TOtherProperty>(result);
}
public static IIncludable<TEntity, TOtherProperty>
ThenInclude<TEntity, TOtherProperty, TProperty>(
this IIncludable<TEntity, ICollection<TProperty>> includes,
Expression<Func<TProperty, TOtherProperty>> propertySelector)
where TEntity : class
{
var result = ((Includable<TEntity, ICollection<TProperty>>)includes)
.IncludableInput.ThenInclude(propertySelector);
return new Includable<TEntity, TOtherProperty>(result);
}
}
}
3.Adım Repository Interface Oluşturulması
Include extension metotlarını etkin kullanmak için şimdi repository’imizi oluşturalım. Öncelikle interfacemizden başlıyoruz. IRepository adlı interfacemiz aşağıdaki gibidir. Gördüğünüz gibi interfacedemizde ki imzalarımızda IIncludable interfacesini implement ettiğimizi görebilirsiniz.
using FriendFinder.Entities.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace FriendFinder.Repository.Generic
{
public interface IRepository<T> where T : IEntity
{
IQueryable<T> Find(Expression<Func<T, bool>> filter = null, Func<IIncludable<T>, IIncludable> includes = null);
T GetById(int id, Func<IIncludable<T>, IIncludable> includes = null);
List<T> GetList(Expression<Func<T, bool>> filter = null, Func<IIncludable<T>, IIncludable> includes = null);
void Create(T entity);
void Update(T entity);
void Delete(int id, bool forceDelete = true);
void DeleteAll(IEnumerable<T> entities);
}
}
4.Adım Repository Sınıfının Oluşturulması
Repository interfacemizide başarıyla oluşturmuş bulunmaktayız ve şimdi repository sınıfımızı oluşturalım.
using FriendFinder.Entities.Entities;
using FriendFinder.Repository.Extensions;
using FriendFinder.Repository.UnitOfWork;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace FriendFinder.Repository.Generic
{
public class Repository<T> : IRepository<T> where T : class, IEntity
{
#region Ctor
private readonly DbSet<T> _dbSet;
public Repository(IUnitOfWork unitOfWork)
{
_dbSet = unitOfWork.GetDbContext().Set<T>();
}
#endregion
public void Create(T entity)
{
entity.CreatedDate = DateTime.Now;
_dbSet.Add(entity);
}
public IQueryable<T> Find(Expression<Func<T, bool>> filter = null, Func<IIncludable<T>, IIncludable> includes = null)
{
var query = _dbSet.AsQueryable();
if (filter != null)
query = query.Where(filter);
if (includes != null)
query = query.IncludeMultiple(includes);
return query;
}
public T GetById(int id, Func<IIncludable<T>, IIncludable> includes = null)
{
var query = _dbSet.AsQueryable();
if (includes != null)
query = query.IncludeMultiple(includes);
return query.Where(x => x.Id == id).FirstOrDefault();
}
public void Update(T entity)
{
entity.ModifiedDate = DateTime.Now;
_dbSet.Update(entity);
}
public void Delete(int id, bool forceDelete = true)
{
var entity = GetById(id);
if (forceDelete)
{
_dbSet.Remove(entity);
}
else
{
entity.StatusId = -1;
Update(entity);
}
}
public void DeleteAll(IEnumerable<T> entities)
{
_dbSet.RemoveRange(entities);
}
public List<T> GetList(Expression<Func<T, bool>> filter = null, Func<IIncludable<T>, IIncludable> includes = null)
{
var query = _dbSet.AsQueryable();
if (filter != null)
query = query.Where(filter);
if (includes != null)
query = query.IncludeMultiple(includes);
return query.ToList();
}
}
}
EKSTRA- 5.Adım Unit Of Work Design Pattern’i Oluşturmak
Bu adımı ekstra olarak kabul edebiliriz. Dilerseniz projenize Unit of Work Design Pattern’ide ekleyebilirsiniz.
Unit of Work Interface
using FriendFinder.Entities.Data;
using System;
namespace FriendFinder.Repository.UnitOfWork
{
public interface IUnitOfWork : IDisposable
{
int Commit();
ApplicationDbContext GetDbContext();
}
}
Unit of Work Sınıfı
using System;
using FriendFinder.Entities.Data;
namespace FriendFinder.Repository.UnitOfWork
{
public class UnitOfWork : IUnitOfWork
{
private bool _disposed;
private readonly ApplicationDbContext _dbContext;
public UnitOfWork(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public int Commit()
{
return _dbContext.SaveChanges();
}
public ApplicationDbContext GetDbContext()
{
return _dbContext;
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_dbContext.Dispose();
}
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
KULLANIMI
Bütün bu adımları başarıyla tamamlamışsanız eğer hadi gelin Include metotlarımızı bir deneyelim. Aşağıdaki örneği inceleyebilirsiniz.
public IEnumerable<MessageDto> GetMessagesByGroupName(string groupName)
{
var chatGroup = _chatGroup.Find(x => x.Name == groupName).FirstOrDefault();
IEnumerable<MessageDto> data = _chatRepository.Find(x => x.ChatGroupId == chatGroup.Id,
x => x.Include(u => u.Sender).ThenInclude(t => t.UserDetail)
.Include(r => r.ChatGroup).ThenInclude(tt => tt.CreatedUser)).ToList().Select(x => new MessageDto
{
Text = x.Text,
CreateDate = x.CreatedDate,
IsRead = x.IsRead,
SenderName = x.Sender.UserName,
SenderId = x.Sender.Id,
ProfilePhotoUrl = x.Sender.UserDetail.ProfilePhotoPath,
GroupName = x.ChatGroup.Name
}).AsEnumerable();
return data;
}
ÖRNEK PROJE
Proje örneği için aşağıdaki Github adresimdeki projeyi inceleyebilirsiniz.