Skip to content

Instantly share code, notes, and snippets.

@Eonasdan
Last active July 11, 2022 06:03
Show Gist options
  • Select an option

  • Save Eonasdan/30533e6d45114ec65d4f8eade5c0094d to your computer and use it in GitHub Desktop.

Select an option

Save Eonasdan/30533e6d45114ec65d4f8eade5c0094d to your computer and use it in GitHub Desktop.
This is my attempt at a very flexible generic repository for an n-layer MVC project.

Generic Repo

This is my attempt at a very flexible generic repository for an n-layer MVC project.

IRepository

I've got this placed in my business/service layer which any DAL (Data Access Layer) project can reference and implement.

PagedEntities

This allows a pageable search result to be used. The reason for the custom class is so that you can return the total count which is needed for a pagination generation and the subset results. I used X.PagedList and something like this in the Controller:

new StaticPagedList<PagedThingDto>(HowEverYouAccessedTheRepo.Entities, model.PageNumber,
    model.ItemsPerPage, HowEverYouAccessedTheRepo.Count);

Note that PagedThingDto should be the same Type as HowEverYouAccessedTheRepo.Entities

BlobDataRepository

An example of a concrete.

ReSharper repo live template

This is a ReSharper live template for creating a concrete of the repo.

This allows for:

  1. Create a new class for your repo (BlobDataRepository.cs)
  2. Remove the default class declaration
  3. Type repo (assuming you saved the live template as repo) and hit tab
  4. Type the Entity name e.g. BlobData, hit enter
  5. Type the pluralized name e.g. BlobDatas, hit enter
  6. Done! Bam! I just saved you 157 lines of code PER Entity!
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using PROJECT.Business.Dto;
using PROJECT.Business.Models;
using PROJECT.Business.Repositories;
//this is an example repository concrete
namespace PROJECT.Data.Repositories
{
public class BlobDataRepository : IRepository<BlobData>
{
private readonly ApplicationDbContext _applicationDbContext;
private bool _disposed;
public BlobDataRepository(ApplicationDbContext applicationDbContext)
{
_applicationDbContext = applicationDbContext;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public async Task DeleteAsync(BlobData t)
{
_applicationDbContext.BlobDatas.Remove(t);
await SaveAsync();
}
public async Task<List<BlobData>> FindByAsync(Expression<Func<BlobData, bool>> predicate,
Func<IQueryable<BlobData>, IOrderedQueryable<BlobData>> orderBy = null)
{
var dbset = _applicationDbContext.BlobDatas.Where(predicate);
if (orderBy != null)
{
return await orderBy(dbset).ToListAsync();
}
return await dbset.ToListAsync();
}
public async Task<List<TResult>> FindByAsync<TResult>(Expression<Func<BlobData, bool>> predicate,
Expression<Func<BlobData, TResult>> select,
Func<IQueryable<BlobData>, IOrderedQueryable<BlobData>> orderBy = null)
{
var dbset = _applicationDbContext.BlobDatas.Where(predicate);
if (orderBy != null)
{
return await orderBy(dbset).Select(select).ToListAsync();
}
return await dbset.Select(select).ToListAsync();
}
public async Task<PagedEntities<BlobData>> FindByPageAsync(Expression<Func<BlobData, bool>> predicate,
int page, int itemsPerPage, Func<IQueryable<BlobData>, IOrderedQueryable<BlobData>> orderBy = null)
{
var skip = itemsPerPage*(page - 1);
if (orderBy == null) orderBy = x => x.OrderBy(y => y.Id); //Skip requires order by
var dbset = orderBy(_applicationDbContext.BlobDatas.Where(predicate));
return new PagedEntities<BlobData>
{
Count = dbset.Count(),
Entities = await dbset.Skip(() => skip).Take(() => itemsPerPage).ToListAsync()
};
}
public async Task<PagedEntities<BlobData>> FindByPageAsync(Expression<Func<BlobData, bool>> predicate,
Expression<Func<BlobData, BlobData>> select, int page, int itemsPerPage,
Func<IQueryable<BlobData>, IOrderedQueryable<BlobData>> orderBy = null)
{
var skip = itemsPerPage*(page - 1);
if (orderBy == null) orderBy = x => x.OrderBy(y => y.Id); //Skip requires order by
var dbset = orderBy(_applicationDbContext.BlobDatas.Where(predicate).Select(select));
return new PagedEntities<BlobData>
{
Count = dbset.Count(),
Entities = await dbset.Skip(() => skip).Take(() => itemsPerPage).ToListAsync()
};
}
public List<BlobData> GetAll(Func<IQueryable<BlobData>, IOrderedQueryable<BlobData>> orderBy = null)
{
return orderBy?.Invoke(_applicationDbContext.BlobDatas).ToList() ??
_applicationDbContext.BlobDatas.ToList();
}
public async Task<List<BlobData>> GetAllAsync(Func<IQueryable<BlobData>, IOrderedQueryable<BlobData>> orderBy = null)
{
if (orderBy != null) return await orderBy(_applicationDbContext.BlobDatas).ToListAsync();
return await _applicationDbContext.BlobDatas.ToListAsync();
}
public async Task<List<TResult>> GetAllAsync<TResult>(Expression<Func<BlobData, TResult>> select,
Func<IQueryable<BlobData>, IOrderedQueryable<BlobData>> orderBy = null)
{
if (orderBy != null) return await orderBy(_applicationDbContext.BlobDatas).Select(select).ToListAsync();
return await _applicationDbContext.BlobDatas.Select(select).ToListAsync();
}
public async Task<BlobData> GetAsync(int id)
{
return await _applicationDbContext.BlobDatas.FindAsync(id);
}
public async Task<TResult> GetAsync<TResult>(int id, Expression<Func<BlobData, TResult>> select)
{
return await _applicationDbContext.BlobDatas.Where(x => x.Id == id).Select(select).FirstOrDefaultAsync();
}
public async Task<BlobData> GetAsync(Expression<Func<BlobData, bool>> predicate)
{
return await _applicationDbContext.BlobDatas.FirstOrDefaultAsync(predicate);
}
public async Task<TResult> GetAsync<TResult>(Expression<Func<BlobData, bool>> predicate,
Expression<Func<BlobData, TResult>> select)
{
return await _applicationDbContext.BlobDatas.Where(predicate).Select(select).FirstOrDefaultAsync();
}
public async Task<BlobData> InsertAsync(BlobData t)
{
_applicationDbContext.BlobDatas.Add(t);
await SaveAsync();
return t;
}
public async Task SaveAsync()
{
await _applicationDbContext.SaveChangesAsync();
}
public async Task UpdateAsync(BlobData t)
{
_applicationDbContext.Entry(t).State = EntityState.Modified;
await SaveAsync();
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_applicationDbContext.Dispose();
}
}
_disposed = true;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using PROJECT.Business.Dto;
namespace PROJECT.Business.Repositories
{
public interface IRepository<T> : IDisposable where T : class
{
/// <summary>
/// Returns all <typeparamref name="T" /> records as a List. Takes an optional <paramref name="orderBy" />.
/// </summary>
/// <param name="orderBy">Example: x => x.OrderBy(y => y.Name).ThenBy(y => y.Profit)</param>
/// <returns></returns>
List<T> GetAll(Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null);
/// <summary>
/// Returns all <typeparamref name="T" /> records as an Async List. Takes an optional <paramref name="orderBy" />.
/// Must be used with await!
/// </summary>
/// <param name="orderBy">Example: x => x.OrderBy(y => y.Name).ThenBy(y => y.Profit)</param>
/// <example>
/// this is an example
/// </example>
/// <returns></returns>
Task<List<T>> GetAllAsync(Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null);
/// <summary>
/// Returns all <typeparamref name="T" /> records as an Async List. Using <paramref name="select" /> to return desire
/// to columns.
/// Takes an optional <paramref name="orderBy" />. Must be used with await!
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="select">Example x => x.Id or x => new {x.Id, x.Name}</param>
/// <param name="orderBy">Example: x => x.OrderBy(y => y.Name).ThenBy(y => y.Profit)</param>
/// <returns></returns>
Task<List<TResult>> GetAllAsync<TResult>(Expression<Func<T, TResult>> select,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null);
/// <summary>
/// Returns a single <typeparamref name="T" /> record based on the <paramref name="id" />.
/// Must be used with await!
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<T> GetAsync(int id);
/// <summary>
/// Returns a single <typeparamref name="T" />.
/// Must be used with await!
/// </summary>
/// <param name="predicate">Where clause</param>
/// <returns></returns>
Task<T> GetAsync(Expression<Func<T, bool>> predicate);
/// <summary>
/// Returns a single <typeparamref name="T" /> using the <paramref name="predicate" /> as a
/// Where clause and using <paramref name="select" /> to return desire to columns.
/// Must be used with await!
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="predicate">Example x => x.Id == 4</param>
/// <param name="select">Example x => x.Id or x => new {x.Id, x.Name}</param>
/// <returns></returns>
Task<TResult> GetAsync<TResult>(Expression<Func<T, bool>> predicate, Expression<Func<T, TResult>> select);
/// <summary>
/// Returns single <typeparamref name="T" /> using <paramref name="select" /> to return desire to columns.
/// Must be used with await!
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="id">Record Id</param>
/// <param name="select">Example x => x.Id or x => new {x.Id, x.Name}</param>
/// <returns></returns>
Task<TResult> GetAsync<TResult>(int id, Expression<Func<T, TResult>> select);
/// <summary>
/// Returns a <see cref="List{T}" /> using the <paramref name="predicate" /> as a Where clause.
/// Takes an optional <paramref name="orderBy" />. Must be used with await!
/// </summary>
/// <param name="predicate">Example x => x.Id == 4</param>
/// <param name="orderBy">Example: x => x.OrderBy(y => y.Name).ThenBy(y => y.Profit)</param>
/// <returns>
/// <see cref="List{T}" />
/// </returns>
Task<List<T>> FindByAsync(Expression<Func<T, bool>> predicate,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null);
/// <summary>
/// Returns a <see cref="List{T}" /> using the <paramref name="predicate" /> as a
/// Where clause and using <paramref name="select" /> to return desire to columns.
/// Takes an optional <paramref name="orderBy" />. Must be used with await!
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="predicate">Example x => x.Id == 4</param>
/// <param name="select">Example x => x.Id or x => new {x.Id, x.Name}</param>
/// <param name="orderBy">Example: x => x.OrderBy(y => y.Name).ThenBy(y => y.Profit)</param>
/// <returns></returns>
Task<List<TResult>> FindByAsync<TResult>(Expression<Func<T, bool>> predicate,
Expression<Func<T, TResult>> select,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null);
/// <summary>
/// Returns <see cref="PagedEntities{T}" />using the <paramref name="predicate" /> as a
/// Where clause and using. Takes an optional <paramref name="orderBy" />. Must be used with await!
/// </summary>
/// <param name="predicate">Example x => x.Id == 4</param>
/// <param name="page">Skip the number of <paramref name="itemsPerPage" /></param>
/// <param name="itemsPerPage">Number of records to retrieve</param>
/// <param name="orderBy">Example: x => x.OrderBy(y => y.Name).ThenBy(y => y.Profit)</param>
/// <returns></returns>
Task<PagedEntities<T>> FindByPageAsync(Expression<Func<T, bool>> predicate, int page, int itemsPerPage,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null);
/// <summary>
/// Returns <see cref="PagedEntities{T}" />using the <paramref name="predicate" /> as a
/// Where clause and using <paramref name="select" /> to return desire to columns.
/// Takes an optional <paramref name="orderBy" />. Must be used with await!
/// </summary>
/// <param name="predicate">Example x => x.Id == 4</param>
/// <param name="select">Example x => x.Id or x => new {x.Id, x.Name}</param>
/// <param name="page">Skip the number of <paramref name="itemsPerPage" /></param>
/// <param name="itemsPerPage">Number of records to retrieve</param>
/// <param name="orderBy"></param>
/// <returns></returns>
Task<PagedEntities<T>> FindByPageAsync(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> select,
int page, int itemsPerPage, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null);
/// <summary>
/// Asynchronously inserts <typeparamref name="T" />, calls <see cref="SaveAsync" /> and returns
/// <typeparamref name="T" />.
/// Must be used with await!
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
Task<T> InsertAsync(T t);
/// <summary>
/// Asynchronously deletes <typeparamref name="T" /> and calls <see cref="SaveAsync" />.
/// Must be used with await!
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
Task DeleteAsync(T t);
/// <summary>
/// Sets <typeparamref name="T" />'s State to Modified and calls <see cref="SaveAsync" />.
/// Must be used with await!
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
Task UpdateAsync(T t);
/// <summary>
/// Calls the DALs save method. Probably should *NOT* be used by itself.
/// Must be used with await!
/// </summary>
/// <returns></returns>
Task SaveAsync();
}
}
using System.Collections.Generic;
using PROJECT.Business.Repositories;
namespace PROJECT.Business.Dto
{
/// <summary>
/// Used in paging methods of <see cref="IRepository{T}"/> to return a total record <see cref="Count"/> of <see cref="Entities"/>
/// </summary>
/// <typeparam name="T"></typeparam>
public class PagedEntities<T>
{
/// <summary>
/// Total record count
/// </summary>
public int Count { get; set; }
/// <summary>
/// Query results
/// </summary>
public IEnumerable<T> Entities { get; set; }
}
}
public class $type$Repository : IRepository<$type$>
{
private readonly ApplicationDbContext _applicationDbContext;
private bool _disposed;
public $type$Repository(ApplicationDbContext applicationDbContext)
{
_applicationDbContext = applicationDbContext;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public async Task DeleteAsync($type$ t)
{
_applicationDbContext.$typePural$.Remove(t);
await SaveAsync();
}
public async Task<List<$type$>> FindByAsync(Expression<Func<$type$, bool>> predicate,
Func<IQueryable<$type$>, IOrderedQueryable<$type$>> orderBy = null)
{
var dbset = _applicationDbContext.$typePural$.Where(predicate);
if (orderBy != null)
{
return await orderBy(dbset).ToListAsync();
}
return await dbset.ToListAsync();
}
public async Task<List<TResult>> FindByAsync<TResult>(Expression<Func<$type$, bool>> predicate,
Expression<Func<$type$, TResult>> select,
Func<IQueryable<$type$>, IOrderedQueryable<$type$>> orderBy = null)
{
var dbset = _applicationDbContext.$typePural$.Where(predicate);
if (orderBy != null)
{
return await orderBy(dbset).Select(select).ToListAsync();
}
return await dbset.Select(select).ToListAsync();
}
public async Task<PagedEntities<$type$>> FindByPageAsync(Expression<Func<$type$, bool>> predicate,
int page, int itemsPerPage, Func<IQueryable<$type$>, IOrderedQueryable<$type$>> orderBy = null)
{
var skip = itemsPerPage*(page - 1);
if (orderBy == null) orderBy = x => x.OrderBy(y => y.Id); //Skip requires order by
var dbset = orderBy(_applicationDbContext.$typePural$.Where(predicate));
return new PagedEntities<$type$>
{
Count = dbset.Count(),
Entities = await dbset.Skip(() => skip).Take(() => itemsPerPage).ToListAsync()
};
}
public async Task<PagedEntities<$type$>> FindByPageAsync(Expression<Func<$type$, bool>> predicate,
Expression<Func<$type$, $type$>> select, int page, int itemsPerPage, Func<IQueryable<$type$>, IOrderedQueryable<$type$>> orderBy = null)
{
var skip = itemsPerPage * (page - 1);
if (orderBy == null) orderBy = x => x.OrderBy(y => y.Id); //Skip requires order by
var dbset = orderBy(_applicationDbContext.$typePural$.Where(predicate).Select(select));
return new PagedEntities<$type$>
{
Count = dbset.Count(),
Entities = await dbset.Skip(() => skip).Take(() => itemsPerPage).ToListAsync()
};
}
public List<$type$> GetAll(Func<IQueryable<$type$>, IOrderedQueryable<$type$>> orderBy = null)
{
return orderBy?.Invoke(_applicationDbContext.$typePural$).ToList() ??
_applicationDbContext.$typePural$.ToList();
}
public async Task<List<$type$>> GetAllAsync(Func<IQueryable<$type$>, IOrderedQueryable<$type$>> orderBy = null)
{
if (orderBy != null) return await orderBy(_applicationDbContext.$typePural$).ToListAsync();
return await _applicationDbContext.$typePural$.ToListAsync();
}
public async Task<List<TResult>> GetAllAsync<TResult>(Expression<Func<$type$, TResult>> select,
Func<IQueryable<$type$>, IOrderedQueryable<$type$>> orderBy = null)
{
if (orderBy != null) return await orderBy(_applicationDbContext.$typePural$).Select(select).ToListAsync();
return await _applicationDbContext.$typePural$.Select(select).ToListAsync();
}
public async Task<$type$> GetAsync(int id)
{
return await _applicationDbContext.$typePural$.FindAsync(id);
}
public async Task<TResult> GetAsync<TResult>(int id, Expression<Func<$type$, TResult>> select)
{
return await _applicationDbContext.$typePural$.Where(x => x.Id == id).Select(select).FirstOrDefaultAsync();
}
public async Task<$type$> GetAsync(Expression<Func<$type$, bool>> predicate)
{
return await _applicationDbContext.$typePural$.FirstOrDefaultAsync(predicate);
}
public async Task<TResult> GetAsync<TResult>(Expression<Func<$type$, bool>> predicate,
Expression<Func<$type$, TResult>> select)
{
return await _applicationDbContext.$typePural$.Where(predicate).Select(select).FirstOrDefaultAsync();
}
public async Task<$type$> InsertAsync($type$ t)
{
_applicationDbContext.$typePural$.Add(t);
await SaveAsync();
return t;
}
public async Task SaveAsync()
{
await _applicationDbContext.SaveChangesAsync();
}
public async Task UpdateAsync($type$ t)
{
_applicationDbContext.Entry(t).State = EntityState.Modified;
await SaveAsync();
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_applicationDbContext.Dispose();
}
}
_disposed = true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment