Skip to content

Instantly share code, notes, and snippets.

@billgeek
Last active February 17, 2018 19:22
Show Gist options
  • Save billgeek/40121f883109c812e1b8b97b7b0ea945 to your computer and use it in GitHub Desktop.
Save billgeek/40121f883109c812e1b8b97b7b0ea945 to your computer and use it in GitHub Desktop.
Generic Repository pattern

Creating a Generic Repository Pattern

The below example shows how to implement a Repository of objects in a generic fashion. This was adapted and simplified from this article: https://code.msdn.microsoft.com/generic-repository-pattern-ddea2262

Overview

The point of this code is to allow the generic access to any object type in the datastore without the need to define a repository for each object. It is important to note that, although the below examples should work as is, it is recommended to make use of a dependency injection framework as database connection limits are not considered.

using LinqKit;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
public class EFRepository<T> : IRepository<T> where T : class
{
protected DbContext Context;
protected readonly DbSet<T> Table;
public EFRepository(DbContext context)
{
Context = context;
Table = Context.Set<T>();
}
public IEnumerable<T> Get(Expression<Func<T, bool>> searchCriteria = null)
{
return searchCriteria == null ? Table.ToList() : Table.Where(searchCriteria).ToList();
}
public ResultPage<T> GetByPage<TKey>(Expression<Func<T, bool>> searchCriteria, Expression<Func<T, TKey>> orderCriteria, bool orderDescending, int pageNumber, int recordsPerPage)
{
// 1. Search for rows (AsExpandable used as sub-queries do not play nice with LINQ to SQL. See here: http://tomasp.net/blog/linq-expand.aspx
var rawResults = searchCriteria == null ? Table : Table.AsExpandable().Where(searchCriteria);
// 2. Apply ordering
if (orderDescending)
{
rawResults = rawResults.OrderByDescending(orderCriteria);
}
else
{
rawResults = rawResults.OrderBy(orderCriteria);
}
// 3. Skip and take pages
var resultingRows = rawResults.Skip((pageNumber - 1) * recordsPerPage).Take(recordsPerPage);
// 4. Return results
return new ResultPage<T>(resultingRows, rawResults.Count(), resultingRows.Count());
}
public T GetByKey(object key)
{
return Table.Find(key);
}
public virtual void Add(T record)
{
Table.Add(record);
}
public virtual void AddMany(IEnumerable<T> records)
{
Table.AddRange(records);
}
public virtual void Update(T record)
{
Table.Attach(record);
Context.Entry(record).State = EntityState.Modified;
}
public void Remove(object key)
{
var record = Table.Find(key);
Table.Remove(record);
}
public void RemoveMany(IEnumerable<T> records)
{
Table.RemoveRange(records);
}
public void RemoveAll()
{
Table.RemoveRange(Table);
}
public virtual void Commit()
{
Context.SaveChanges();
}
public int RowCount(Expression<Func<T, bool>> filterCriteria = null)
{
if (filterCriteria == null) filterCriteria = t => true;
return Table.Count(filterCriteria);
}
public void Dispose()
{
Context.Dispose();
}
}
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
public interface IRepository<T> : IDisposable where T : class
{
// Returns number of rows where the criteria matches
int RowCount(Expression<Func<T, bool>> filterCriteria = null);
// Retrieves records based on criteria
IEnumerable<T> Get(Expression<Func<T, bool>> searchCriteria = null);
// Retrieves subset of records based on criteria
ResultPage<T> GetByPage<TKey>(Expression<Func<T, bool>> searchCriteria, Expression<Func<T, TKey>> orderCriteria, bool orderDescending, int pageNumber, int recordsPerPage);
// Retrieves single record based on unique key
T GetByKey(object key);
// Adds record to table
void Add(T record);
// Adds many records to table
void AddMany(IEnumerable<T> records);
// Updates object
void Update(T record);
// Removes single Object
void Remove(object key);
// Removes many objects
void RemoveMany(IEnumerable<T> records);
// Removes all objects
void RemoveAll();
// Updates all changed objects
void Commit();
}
using System.Collections.Generic;
using System.Runtime.Serialization;
[DataContract]
public class ResultPage<T> where T : class
{
public ResultPage(IEnumerable<T> result, long? totalRecords, long? returnedRecords)
{
TotalRecords = totalRecords;
ReturnedRecords = returnedRecords;
Results = result;
}
public ResultPage() : this(null, null, null)
{
}
[DataMember(Name = "results")]
public IEnumerable<T> Results { get; }
[DataMember(Name = "totalRecords")]
public long? TotalRecords { get; }
[DataMember(Name = "returnedRecords")]
public long? ReturnedRecords { get; }
}
// A few examples of how to use the generic repository
// Note that you probably would want to do all of this using a DI framework...
public class UserService
{
private readonly IRepository<User> _userRepository;
public UserService()
{
// Ideally use Dependency Injection; If you have many inter-dependent services / modules, you will start running out of available DB connections
_userRepository = new EFRepository<User>(new MyDatabaseContext());
}
public List<UserDto> GetUsers()
{
return _userRepository.Get();
}
public UserDto CreateUser(UserDto toCreate)
{
var newUser = new User();
newUser.FirstName = toCreate.FirstName;
newUser.LastName = toCreate.LastName;
newUser.UserName = toCreate.UserName;
_userRepository.Add(newUser);
_userRepository.Commit();
return newUser;
}
public UserDto UpdateUser(UserDto toUpdate)
{
var entity = _userRepository.GetByKey(toUpdate.Id);
if (entity != null)
{
entity.FirstName = toUpdate.FirstName;
entity.LastName = toUpdate.LastName;
entity.UserName = toUpdate.UserName;
_userRepository.Update(entity);
_userRepository.Commit();
}
return null;
}
public void RemoveUser(int userId)
{
_userRepository.Remove(userId);
_userRepository.Commit();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment