Skip to content

Instantly share code, notes, and snippets.

@jasonmitchell
Last active September 13, 2015 17:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jasonmitchell/62905f39a21979f14f06 to your computer and use it in GitHub Desktop.
Save jasonmitchell/62905f39a21979f14f06 to your computer and use it in GitHub Desktop.
Generic Repository
namespace Sample.Repositories.Specifications
{
using System;
using System.Linq;
using System.Linq.Expressions;
internal class AndSpecification<T> : ISpecification<T>
{
private readonly ISpecification<T> left;
private readonly ISpecification<T> right;
public AndSpecification(ISpecification<T> left, ISpecification<T> right)
{
this.left = left;
this.right = right;
}
public Expression<Func<T, bool>> IsSatisifiedBy()
{
var leftExpression = left.IsSatisifiedBy();
var rightExpression = right.IsSatisifiedBy();
var parameter = leftExpression.Parameters.Single();
var body = Expression.AndAlso(leftExpression.Body, SpecificationParameterRebinder.ReplaceParameter(rightExpression.Body, parameter));
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
}
}
namespace Sample.Specifications
{
using System;
using System.Linq.Expressions;
using Repositories.Models;
using Repositories.Specifications;
public class GetByFirstName : ISpecification<Person>
{
private readonly string firstName;
public GetByFirstName(string firstName)
{
this.firstName = firstName;
}
public Expression<Func<Person, bool>> IsSatisifiedBy()
{
return x => x.FirstName == firstName;
}
}
}
namespace Sample.Specifications
{
using System;
using System.Linq.Expressions;
using Repositories.Models;
using Repositories.Specifications;
public class GetByLastName : ISpecification<Person>
{
private readonly string lastName;
public GetByLastName(string lastName)
{
this.lastName = lastName;
}
public Expression<Func<Person, bool>> IsSatisifiedBy()
{
return x => x.LastName == lastName;
}
}
}
namespace Sample.Repositories.Specifications
{
using System;
using System.Linq.Expressions;
public interface ISpecification<T>
{
Expression<Func<T, bool>> IsSatisifiedBy();
}
}
namespace Sample.Repositories.Specifications
{
using System;
using System.Linq.Expressions;
internal class NotSpecification<T> : ISpecification<T>
{
private readonly ISpecification<T> specification;
public NotSpecification(ISpecification<T> specification)
{
this.specification = specification;
}
public Expression<Func<T, bool>> IsSatisifiedBy()
{
var expression = specification.IsSatisifiedBy();
var parameter = expression.Parameters[0];
var body = Expression.Not(expression.Body);
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
}
}
namespace Sample.Repositories.Specifications
{
using System;
using System.Linq;
using System.Linq.Expressions;
internal class OrSpecification<T> : ISpecification<T>
{
private readonly ISpecification<T> left;
private readonly ISpecification<T> right;
public OrSpecification(ISpecification<T> left, ISpecification<T> right)
{
this.left = left;
this.right = right;
}
public Expression<Func<T, bool>> IsSatisifiedBy()
{
var leftExpression = left.IsSatisifiedBy();
var rightExpression = right.IsSatisifiedBy();
var parameter = leftExpression.Parameters.Single();
var body = Expression.OrElse(leftExpression.Body, SpecificationParameterRebinder.ReplaceParameter(rightExpression.Body, parameter));
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
}
}
namespace Sample
{
using System;
using System.Configuration;
using Repositories;
using Repositories.Models;
using Repositories.Specifications;
using Specifications;
class Program
{
static void Main(string[] args)
{
var connectionString = ConfigurationManager.ConnectionStrings["Demo"].ConnectionString;
using (var context = new SampleContext(connectionString))
{
// IQUERYABLE REPOSITORY
var repository = new QueryableRepository(context);
var people = repository.Query<Person>(x => x.FirstName == "Jason").ToList();
// OR
//var people = repository.Query<Person>().Where(x => x.FirstName == "Jason").ToList();
// WHY NOT JUST USE THE CONTEXT??
//var people = context.People.Where(x => x.FirstName == "Jason").ToList();
Console.WriteLine("Found {0} people", people.Count);
Console.ReadLine();
}
}
}
}
namespace Sample
{
using System;
using System.Configuration;
using Repositories;
using Repositories.Models;
using Repositories.Specifications;
using Specifications;
class Program
{
static void Main(string[] args)
{
var connectionString = ConfigurationManager.ConnectionStrings["Demo"].ConnectionString;
using (var context = new SampleContext(connectionString))
{
var repository = new SpecificationRepository(context);
var specification = new GetByFirstName("Jason").And(new GetByLastName("Mitchell"));
var people = repository.Find(specification);
Console.WriteLine("Found {0} people", people.Count);
Console.ReadLine();
}
}
}
}
namespace Sample.Repositories
{
using System;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
public class QueryableRepository
{
private readonly DbContext context;
public QueryableRepository(DbContext context)
{
this.context = context;
}
public IQueryable<T> Query<T>(Expression<Func<T, bool>> predicate = null) where T : class
{
var query = (IQueryable<T>)context.Set<T>();
if (predicate != null)
{
query = query.Where(predicate);
}
return query;
}
}
}
namespace Sample.Repositories.Specifications
{
using System.Linq.Expressions;
internal class SpecificationParameterRebinder : ExpressionVisitor
{
private readonly ParameterExpression specificationParameter;
private SpecificationParameterRebinder(ParameterExpression specificationParameter)
{
this.specificationParameter = specificationParameter;
}
public static Expression ReplaceParameter(Expression expression, ParameterExpression parameter)
{
return new SpecificationParameterRebinder(parameter).Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression p)
{
return base.VisitParameter(specificationParameter);
}
}
}
namespace Sample.Repositories
{
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using Specifications;
public class SpecificationRepository
{
private readonly DbContext context;
public SpecificationRepository(DbContext context)
{
this.context = context;
}
public ICollection<T> Find<T>(ISpecification<T> specification) where T : class
{
var query = context.Set<T>().Where(specification.IsSatisifiedBy());
return query.ToList();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment