Skip to content

Commit

Permalink
composed spec - parameter replacement (#1)
Browse files Browse the repository at this point in the history
* composed spec - parameter replacement

Fixes invalid op exception when composing specs.

* connection string back to .

Sorry -- forgot to remove my connection string and put yours back in.
  • Loading branch information
engstrocity authored and vkhorikov committed Jul 5, 2017
1 parent bcf120e commit 1508638
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 31 deletions.
25 changes: 25 additions & 0 deletions SpecificationPattern.Tests/ComposedSpecificationTests.cs
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Should;
using Xunit;

namespace SpecificationPattern.Tests {

public class ComposedSpecificationTests {

[Fact]
public void T1() {
var movie = new Movie("Some Movie", new DateTime(2010, 2, 1), MpaaRating.G, "Triller", 10);
var pg13Rating = new MpaaRatingAtMostSpecification(MpaaRating.PG13);
var goodMovie = new GoodMovieSpecification();
var composed = pg13Rating.And(goodMovie);

bool isSatisfiedBy = composed.IsSatisfiedBy(movie);

isSatisfiedBy.ShouldEqual(true);
}
}
}
Expand Up @@ -60,6 +60,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="ComposedSpecificationTests.cs" />
<Compile Include="Tests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
Expand Down
16 changes: 16 additions & 0 deletions SpecificationPattern/ParameterReplacer.cs
@@ -0,0 +1,16 @@
using System.Linq.Expressions;

namespace SpecificationPattern {

internal class ParameterReplacer : ExpressionVisitor {

private readonly ParameterExpression _parameter;

protected override Expression VisitParameter(ParameterExpression node)
=> base.VisitParameter(_parameter);

internal ParameterReplacer(ParameterExpression parameter) {
_parameter = parameter;
}
}
}
2 changes: 1 addition & 1 deletion SpecificationPattern/SessionFactory.cs
Expand Up @@ -14,7 +14,7 @@ namespace SpecificationPattern
{
public static class SessionFactory
{
private const string ConnectionString = "Server=.;Database=SpecificationPattern;Trusted_Connection=true;";
private const string ConnectionString = @"Server=.;Database=SpecificationPattern;Trusted_Connection=true;";

private static readonly ISessionFactory _factory;

Expand Down
1 change: 1 addition & 0 deletions SpecificationPattern/SpecificationPattern.csproj
Expand Up @@ -57,6 +57,7 @@
<Compile Include="Movie.cs" />
<Compile Include="MovieMap.cs" />
<Compile Include="MovieRepository.cs" />
<Compile Include="ParameterReplacer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SessionFactory.cs" />
<Compile Include="Specifications.cs" />
Expand Down
55 changes: 25 additions & 30 deletions SpecificationPattern/Specifications.cs
@@ -1,81 +1,76 @@
using System;
using System.Linq;
using System.Linq.Expressions;


namespace SpecificationPattern
{
public abstract class Specification<T>
{
namespace SpecificationPattern {

public abstract class Specification<T> {

public abstract Expression<Func<T, bool>> ToExpression();


public bool IsSatisfiedBy(T entity)
{
public bool IsSatisfiedBy(T entity) {
Func<T, bool> predicate = ToExpression().Compile();
return predicate(entity);
}


public Specification<T> And(Specification<T> specification)
{
public Specification<T> And(Specification<T> specification) {
return new AndSpecification<T>(this, specification);
}


public Specification<T> Or(Specification<T> specification)
{
public Specification<T> Or(Specification<T> specification) {
return new OrSpecification<T>(this, specification);
}
}


public class AndSpecification<T> : Specification<T>
{
public class AndSpecification<T> : Specification<T> {
private readonly Specification<T> _left;
private readonly Specification<T> _right;


public AndSpecification(Specification<T> left, Specification<T> right)
{
public AndSpecification(Specification<T> left, Specification<T> right) {
_right = right;
_left = left;
}


public override Expression<Func<T, bool>> ToExpression()
{
public override Expression<Func<T, bool>> ToExpression() {
Expression<Func<T, bool>> leftExpression = _left.ToExpression();
Expression<Func<T, bool>> rightExpression = _right.ToExpression();

BinaryExpression andExpression = Expression.AndAlso(leftExpression.Body, rightExpression.Body);
var paramExpr = Expression.Parameter(typeof(T));
var exprBody = Expression.AndAlso(leftExpression.Body, rightExpression.Body);
exprBody = (BinaryExpression)new ParameterReplacer(paramExpr).Visit(exprBody);
var finalExpr = Expression.Lambda<Func<T, bool>>(exprBody, paramExpr);

return Expression.Lambda<Func<T, bool>>(andExpression, leftExpression.Parameters.Single());
return finalExpr;
}
}


public class OrSpecification<T> : Specification<T>
{
public class OrSpecification<T> : Specification<T> {
private readonly Specification<T> _left;
private readonly Specification<T> _right;


public OrSpecification(Specification<T> left, Specification<T> right)
{
public OrSpecification(Specification<T> left, Specification<T> right) {
_right = right;
_left = left;
}


public override Expression<Func<T, bool>> ToExpression()
{
Expression<Func<T, bool>> leftExpression = _left.ToExpression();
Expression<Func<T, bool>> rightExpression = _right.ToExpression();

BinaryExpression andExpression = Expression.OrElse(leftExpression.Body, rightExpression.Body);
public override Expression<Func<T, bool>> ToExpression() {
var leftExpression = _left.ToExpression();
var rightExpression = _right.ToExpression();
var paramExpr = Expression.Parameter(typeof(T));
var exprBody = Expression.OrElse(leftExpression.Body, rightExpression.Body);
exprBody = (BinaryExpression)new ParameterReplacer(paramExpr).Visit(exprBody);
var finalExpr = Expression.Lambda<Func<T, bool>>(exprBody, paramExpr);

return Expression.Lambda<Func<T, bool>>(andExpression, leftExpression.Parameters.Single());
return finalExpr;
}
}
}

0 comments on commit 1508638

Please sign in to comment.