I want to modify existing generic repository to add optional select
functionality like in Entity Framework Core.
Desired result:
private readonly IUnitOfWork _unit;
// ...
// without using select functionality
IEnumerable<Entity> entities = await _unit.Account.AllAsync();
Entity? x = await _unit.Account.SingleAsync(x => x == id);
// using select functionality
IEnumerable<DTO> y = await _unit.Account.AllAsync(select: x => new DTO
{
Name = x.Name
});
DTO? y = await _unit.Account.SingleAsync(x => x == id, select: x => new DTO
{
Name = x.Name
});
I tried to implement solution from this question Select specific columns in a generic repository function but parameter was required and I want it to be optional.
For simplicity I only leave methods to which I want to add this functionality in generic repository:
in IBaseRepository.cs
public interface IBaseRepository<T> where T : BaseEntity
{
Task<IEnumerable<T>> AllAsync(
Expression<Func<T, bool>>? filter = null,
Func<IQueryable<T>, IOrderedQueryable<T>>? order = null,
Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
int skip = 0,
int take = int.MaxValue,
Track track = Track.NoTracking);
Task<T?> SingleAsync(
Expression<Func<T, bool>> filter, Func<IQueryable<T>,
IIncludableQueryable<T, object>>? include = null,
Track track = Track.Tracking);
}
in BaseRepository.cs
public class BaseRepository<T> : IBaseRepository<T> where T : BaseEntity
{
private readonly DataContext _context;
internal DbSet<T> _set;
public BaseRepository(DataContext context)
{
_context = context;
_set = context.Set<T>();
}
public async Task<IEnumerable<T>> AllAsync(
Expression<Func<T, bool>>? filter = null,
Func<IQueryable<T>, IOrderedQueryable<T>>? order = null,
Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
int skip = 0, int take = int.MaxValue, Track track = Track.NoTracking)
{
IQueryable<T> query = _set;
switch (track)
{
case Track.NoTracking:
query = query.AsNoTracking();
break;
case Track.NoTrackingWithIdentityResolution:
query = query.AsNoTrackingWithIdentityResolution();
break;
default:
query = query.AsTracking();
break;
}
query = skip == 0 ? query.Take(take) : query.Skip(skip).Take(take);
query = filter is null ? query : query.Where(filter);
query = order is null ? query : order(query);
query = include is null ? query : include(query);
return await query.ToListAsync();
}
public async Task<T?> SingleAsync(
Expression<Func<T, bool>> filter,
Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
Track track = Track.Tracking)
{
IQueryable<T> query = _set;
switch (track)
{
case Track.NoTracking:
query = query.AsNoTracking();
break;
case Track.NoTrackingWithIdentityResolution:
query = query.AsNoTrackingWithIdentityResolution();
break;
default:
query = query.AsTracking();
break;
}
query = filter is null ? query : query.Where(filter);
query = include is null ? query : include(query);
return await query.SingleOrDefaultAsync();
}
}
in BaseEntity.cs
(every class-dbtable would inherit from BaseEntity
)
public abstract class BaseEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public DateTime CreatedAt { get; set; } = DateTime.Now;
public DateTime? UpdatedAt { get; set; }
}