Raffinert.Spec is a rethinking of libraries and sources such as:
The main goal was to create a simple replacement of LINQKit library without any Entity Framework specific tweaks (see the LINQKit vs Spec Comparison and discussion about embedding linqKit into EfCore). Please treat the specification as an expression constructor.
With Raffinert.Spec you can:
- Combine expressions with logical operators OR, AND, NOT.
- Use nested specifications by calling
IsSatisfiedBymethod. - Create specification templates and apply them to different entities with similar signatures.
Detailed examples can be found in Integration Tests
You can define specifications either inline or create custom specification classes. Below is an example of a custom specification for filtering products by name:
using Raffinert.Spec;
using System.Linq.Expressions;
public class ProductNameSpec : Spec<Product>
{
private readonly string _name;
public ProductNameSpec(string name)
{
_name = name;
}
public override Expression<Func<Product, bool>> GetExpression()
{
return product => product.Name == _name;
}
}Specification templates allow you to define reusable structures that can be adapted to different entities with similar properties.
var template = SpecTemplate<Product>.Create(p => new { p.Name, p.Price }, t => t.Name == "Banana" && t.Price > 10);
var adaptedSpec = template.Adapt<InventoryItem>();In this example, a specification template is created for Product, filtering based on Name and Price. The template is then adapted to an InventoryItem type with matching properties.
To prevent runtime errors when using SpecTemplate, we provide Roslyn Raffinert.Spec.Analyzer that:
- Ensure
SpecTemplate<TSample>.Adapt<TN>()only adapts to types that contain all required members. - Validate that
SpecTemplate.Create(...)uses either an anonymous type projection (e.g.,p => new { p.Name }) or class initialize statement (e.g.,p => new Template{ Name = p.Name }).
These analyzers catch issues at compile-time, improving reliability and maintainability.
The Spec<T> class includes built-in debugging support with a custom debugger display, giving developers an immediate view of the underlying expression while debugging.
See also Raffinert.Proj library.