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.
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.