Specification pattern implementation in C#
This repository provides a C# Composite based implementation of the Specification pattern.
The Composite implementation adds flexibility by allowing the business rules to be combined. In additional to the logical operations, this implementation uses a Builder pattern with Fluent Interfaces. As a result, we can use extensions methods, instead of classes, to create new rules.
The examples below compares the standard composite implementation to an implementation using the builder and fluent interfaces.
In a standard implementation, the rule definition is located inside the class:
public class BlogCreatedAfterSpecification : BaseSpecification<Blog>
{
   public BlogCreatedAfterSpecification(DateTime dateTime)
       : base(x => x.Created >= dateTime)
   {
   }
}This is how to instantiate and build the specifications:
var createdAfter = new BlogCreatedAfterSpecification(dateTime);
var notBanned = new BlogNotBannedSpecification();
var notExpired = new BlogNotExpiredSpecification();
var specification = createdAfter.And(notBanned).And(notExpired);Instead of a class per rule, a extension method is used to add rules. In the sample code below, Add is a method with an Expression parameter, it create an instance of the Specification and add to the composite structure.
public static class BlogSpecificationExtensions
{
    public static ISpecification<Blog> CreatedAfter(this ISpecification<Blog> specification,
           DateTime dateTime)
    {
        return specification.And(x => x.Created >= dateTime);
    }
}The composed rule can be instantiated like below. The result is that the business rules can be read more fluently.
var specification = SpecificationBuilder<Blog>.Create()
                        .CreatedAfter(dateTime)
                        .NotBanned()
                        .NotExpired();dotnet core >= 2.0
This repository provides a console application in additional to an xUnit and a Specflow test project. Please note that Specflow project only works in Windows environment.
Running the console application:
cd src/SpecificationDemo
dotnet ef migrations add InitialCreate --startup-project ../SpecificationDemoConsole
dotnet ef database update --startup-project ../SpecificationDemoConsole
dotnet runRunning tests:
dotnet test SpecificationDemoXunitTest
dotnet test SpecificationDemoBddTestRequirements:
LinqPad >= 5.26
Entity Framework 7 (EF Core) Driver >= 2.0.1
Before configuring the connection, you need to publish one project:
cd src/SpecificationDemoConsole
dotnet publishThen configure the connection following the steps below:
- Add a new connection
- Use a typed data context using Entity Framework 2.0.1
 - In the Path to custom assembly, select the publish directory. Example:
D:\dotnet-specification-pattern\src\SpecificationDemoConsole\bin\Debug\netcoreapp2.0\SpecificationDemo.dll - Select the Full type name of the typed DbContext: SpecificationDemo.Data.BloggingContext
 - Select Via a constructor that accepts a string:
Server=(localdb)\mssqllocaldb;Database=Blog;Trusted_Connection=True;ConnectRetryCount=0 - Select Remember this connection
 
 
Create a new query and change to C# Program. Then, select the BloggingContext in SpecificationDemo.dll connection.
In query properties (F4), add the following namespaces:
SpecificationDemo.Data
SpecificationDemo.Entities
SpecificationDemo.Specifications
System
System.Data
System.Linq
System.Threading.Tasks
LinqPad C# Program sample code :
void Main()
{
    var blogRepository = new EfReadRepository<Blog>(this);
    
    var specification = SpecificationBuilder<Blog>.Create()
        .NotExpired()
        .CreatedAfter(new DateTime(2017, 1, 1));
        
    var result = blogRepository
        .Where(specification, b => b.Posts)
        .ToList();
        
    Console.WriteLine(result);
}Using C# Statement(s) this can be simplified to:
var specification = SpecificationBuilder<Blog>.Create()
    .NotExpired()
    .CreatedAfter(new DateTime(2017, 1, 1));
    
Console.Write(Blogs.Where(specification));Copyright (c) 2018 André Gomes
Licensed under the MIT License.