Home > Design Patterns, dotnet 4, SOLID > Composite Pattern using Linq Expressions

Composite Pattern using Linq Expressions

I hope you guys have enjoyed by previous posts on Specification Pattern and the Composition Pattern as this post is an extension to those post and I recommend you read those post before you read this one.

Composition Pattern

Specification Pattern

In this post I am going to take both patterns and apply some Linq expression as I always try to refactoring code and improve coding style every time I get a chance.

In the Specification Pattern post we started with a simple interface ISpecification as following:

ISpecification.cs

public interface ISpecification<T>
{
bool IsSatisfiedBy(T candidate);
ISpecification<T> And(ISpecification<T> other);
ISpecification<T> Or(ISpecification<T> other);
ISpecification<T> Not();
}

And this interface is the core of the Composition Pattern so lets modify this interface so that we can use the Linq Expression provided by .NET.

ISpecificationWithExpression.cs

public interface ISpecificationWithExpression<T>
{
Expression<Func<T, bool>> Predicate { get; }
bool IsSatisfiedBy(T candidate);
ISpecificationWithExpression<T> And(ISpecificationWithExpression<T> other);
ISpecificationWithExpression<T> Or(ISpecificationWithExpression<T> other);
ISpecificationWithExpression<T> Not();
}

The idea is that we are going to use Linq Expression and its internal expression building logic to chain our conditions and because we are using Expression<Func<T, bool>> we can specify our criteria (condition) as a lambada Expression.

Lets move on and first implement the interface to see how our code structure looks like, I always prefer it this way so that I can see the bigger picture without getting into implementation specifics.

public class CompositionWithExpression<T> : ISpecificationWithExpression<T>
{
public Expression<Func<T, bool>> Predicate { get { } }

public bool IsSatisfiedBy(T candidate)
{

}

public ISpecificationWithExpression<T> And(ISpecificationWithExpression<T> other)
{

}

public ISpecificationWithExpression<T> Or(ISpecificationWithExpression<T> other)
{

}

public ISpecificationWithExpression<T> Not()
{

}
}

So this gives us a base structure of our class and now all what we have to do is implement the And(), Or() and the Not() methods using Linq Expressions and its compositions.

CompositionWithExpression.cs

public class CompositionWithExpression<T> : ISpecificationWithExpression<T>
{
private readonly Expression<Func<T, bool>> _predicate;

public CompositionWithExpression(Expression<Func<T, bool>> predicate)
{
_predicate = predicate;
}

public Expression<Func<T, bool>> Predicate
{
get { return _predicate; }
}

public bool IsSatisfiedBy(T entity)
{
return _predicate.Compile().Invoke(entity);
}

public ISpecificationWithExpression<T> And (ISpecificationWithExpression<T> other)
{
var otherInvoke = Expression.Invoke( other.Predicate, 
this._predicate.Parameters);

var newExpression = Expression.MakeBinary(ExpressionType.AndAlso, 
_predicate.Body, otherInvoke);

return 
new CompositionWithExpression<T>(Expression.Lambda<Func<T, bool>>
(newExpression, 
_predicate.Parameters));
}

public ISpecificationWithExpression<T> Or(ISpecificationWithExpression<T> other)
{
var otherInvoke = Expression.Invoke(other.Predicate, 
this._predicate.Parameters);

var newExpression = Expression.MakeBinary(ExpressionType.OrElse, 
_predicate.Body, otherInvoke);

return 
new CompositionWithExpression<T>(Expression.Lambda<Func<T, bool>>
(newExpression, 
_predicate.Parameters));
}

public ISpecificationWithExpression<T> Not()
{
var negateExpression = Expression.Lambda<Func<T, bool>>
(Expression.Not(_predicate.Body), 
_predicate.Parameters);

return new CompositionWithExpression<T>(negateExpression);
}
}

So far what we have done is that we have combined Lambada Expression based on method operation like Or,And and Not.

Lets write some unit test and see how we can use lambada expression to specify the condition.

[TestClass]
public class When_using_Specification_With_Expression
{
[TestMethod]
public void Should_be_able_to_invoke_or_condition()
{
var employee = BuildData.GetEmployee;

var specification = new CompositionWithExpression<Employee>
(e => e.FirstName.StartsWith("T"));

var anotherSpecification = new CompositionWithExpression<Employee>
(e => e.Address.Count > 0);

var newExpression = specification.Or(anotherSpecification);

Assert.AreEqual(true, newExpression.IsSatisfiedBy(employee));
}

[TestMethod]
public void Should_be_able_to_invoke_an_and_condition()
{
var employee = BuildData.GetEmployee;
employee.Address = BuildData.GetAddress;

var specification = new CompositionWithExpression<Employee>
(e => e.FirstName.StartsWith("T"));

var andSpecification = new CompositionWithExpression<Employee>
(e => e.Address.Count > 0);

var compositeSpecification = specification.And(andSpecification);
Assert.AreEqual(true, compositeSpecification.IsSatisfiedBy(employee));
}

[TestMethod]
public void Should_be_able_to_invoke_a_not_condition()
{
var employee = BuildData.GetEmployee;
employee.Address = BuildData.GetAddress;

var specification = new CompositionWithExpression<Employee>
(e => e.FirstName.StartsWith("T"));

var negateSpecification = specification.Not();
Assert.AreEqual(false, negateSpecification.IsSatisfiedBy(employee));
}

}

and this is how the employee class looks like

public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual IList<Address> Address { get; set; }
}

As you can see by the virtue of the fact that the CompositionWithExpression takes a Expression<Func<T, bool>> as constructor parameter,I can pass a lambada expression to specify the condition and it’s so easy to understand.

I think this has been a very lengthy post in terms of code so I will break it up into another post where I will revisit creating different specification and chain them in our entity class the way we did it for Customer class in the Composition Pattern post.

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: