Last active
April 5, 2018 22:55
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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