Skip to content

Instantly share code, notes, and snippets.

@daerogami
Last active April 5, 2018 22:55
Show Gist options
  • Save daerogami/6650a99d9147d2b33de847e3aa14314c to your computer and use it in GitHub Desktop.
Save daerogami/6650a99d9147d2b33de847e3aa14314c to your computer and use it in GitHub Desktop.
Example of a Repository pattern with a filter-able and order-able Get() method
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
//Example classes for repository (Domain)
namespace SomeRepoExample
{
public class Foo
{
public string Name { get; }
public DateTime DateCreated { get; }
public bool IsPotato { get; }
protected IEnumerable<Bar> Bars { get; private set; }
public static Expression<Func<Foo, T>> GetPropertyFromInternal<T>(Expression<Func<Bar, T>> getPropertyExpression)
{
return GetRecentBarExpr().Compose(getPropertyExpression);
}
private static Expression<Func<Foo, Bar>> GetRecentBarExpr()
{
return foo => foo.Bars.OrderByDescending(r => r.SomeDate).FirstOrDefault();
}
}
public class Bar
{
public DateTime SomeDate { get; }
}
}
using System;
using System.Linq.Expressions;
// Helpers for composing expressions (Common)
namespace SomeRepoExample
{
public static class ExpressionExtensionMethods
{
public static Expression Replace(this Expression ex, Expression from, Expression to)
{
return new ReplaceVisitor(from, to).Visit(ex);
}
public static Expression<Func<T, TResult>> Compose<T, TIntermediate, TResult>(this Expression<Func<T, TIntermediate>> first, Expression<Func<TIntermediate, TResult>> second)
{
var lambda = Expression.Lambda<Func<T, TResult>>(
second.Body.Replace(second.Parameters[0], first.Body),
first.Parameters[0]);
return lambda;
}
}
public class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression _from, _to;
public ReplaceVisitor(Expression from, Expression to)
{
_from = from;
_to = to;
}
public override Expression Visit(Expression ex)
{
return ex == _from ? _to : base.Visit(ex);
}
}
}
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
namespace SomeRepoExample
{
// Repository items (Data Access)
public interface IFooRepository
{
IEnumerable<Foo> GetFoos(GetFoosRequest request);
}
public enum GetFoosOrder { None, Created, Name, Bar, IsPotato }
public class GetFoosRequest
{
public bool Ascending { get; }
public string NameFilter { get; }
public DateTime? CreatedFilter { get; }
public bool? IsPotatoFilter { get; }
public GetFoosOrder OrderBy { get; }
}
public class FooRepository : IFooRepository
{
private readonly DbSet<Foo> _foos;
public FooRepository(DbContext ctx)
{
_foos = ctx.Set<Foo>();
}
public IEnumerable<Foo> GetFoos(GetFoosRequest request)
{
var filteredFoos = FilterFoos(request, _foos);
var orderedFoos = OrderFoos(request, filteredFoos);
return orderedFoos;
}
private static IQueryable<Foo> FilterFoos(GetFoosRequest request, IQueryable<Foo> foos)
{
if (!string.IsNullOrEmpty(request.NameFilter))
{
foos = foos.Where(x => x.Name.Contains(request.NameFilter));
}
if (request.CreatedFilter.HasValue)
{
foos = foos.Where(x => x.DateCreated == request.CreatedFilter.Value);
}
if (request.IsPotatoFilter.HasValue)
{
foos = foos.Where(x => x.IsPotato == request.IsPotatoFilter.Value);
}
return foos;
}
// Only filters single column at a time as this was designed around angularjs's ngTable
private static IOrderedQueryable<Foo> OrderFoos(GetFoosRequest request, IQueryable<Foo> foos)
{
switch (request.OrderBy)
{
case GetFoosOrder.None:
case GetFoosOrder.Created:
{
Expression<Func<Foo, DateTime>> sortExp = x => x.DateCreated;
return request.Ascending ? foos.OrderBy(sortExp) : foos.OrderByDescending(sortExp);
}
case GetFoosOrder.Name:
{
Expression<Func<Foo, string>> sortExp = x => x.Name;
return request.Ascending ? foos.OrderBy(sortExp) : foos.OrderByDescending(sortExp);
}
case GetFoosOrder.Bar:
{
var sortExp = Foo.GetPropertyFromInternal(x => x.SomeDate);
return request.Ascending ? foos.OrderBy(sortExp) : foos.OrderByDescending(sortExp);
}
case GetFoosOrder.IsPotato:
{
Expression<Func<Foo, bool>> sortExp = x => x.IsPotato;
return request.Ascending ? foos.OrderBy(sortExp) : foos.OrderByDescending(sortExp);
}
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment