-
Notifications
You must be signed in to change notification settings - Fork 248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add FAQ entry on combining specs #380
base: main
Are you sure you want to change the base?
Conversation
The approach with predicates won't work. EF will throw an exception during the evaluation. Instead, we can write extensions to the builder. public static class CustomerSpecExtensions
{
public static ISpecificationBuilder<Customer> IsAdult(this ISpecificationBuilder<Customer> builder)
=> builder.Where(x => x.Age >= 18);
public static ISpecificationBuilder<Customer> IsAtLeastYearsOld(this ISpecificationBuilder<Customer> builder, int years)
=> builder.Where(x => x.Age >= years);
}
public class AdultCustomersByNameSpec : Specification<Customer>
{
public AdultCustomersByNameSpec(string nameSubstring)
{
Query.IsAdult()
.Where(x => x.Name.Contains(nameSubstring));
}
} We have more examples in the sample apps. https://github.com/ardalis/Specification/blob/main/sample/Ardalis.Sample.Domain/Specs/CustomerSpecExtensions.cs |
|
||
We don't recommend or support combining *specifications*, because doing so moves query logic out of the domain model and into the consumer (typically UI) layer. Choosing which predicates to AND or OR or NOT together is basically the logic that specifications are meant to encapsulate. Instead, if you have predicates you want to share, these can be shared between Specifications by simply creating (static) properties of type `Func<T,bool>` and then using these within your well-named specifications. It's worth remembering that our implementation of Specification has more scope than merely an `IsSatisfiedby()` method, and that operations like `Skip`, `Take`, `OrderBy`, etc. are within that scope but are not combinable using generic logical operations like predicates are. | ||
|
||
However, that's not to say there's no way to extract common predicate logic and reuse it. The recommended approach is to extract the predicate logic (if needed) and reuse that between specifications but still within your domain model. If you find value in using something like a [PredicateBuilder](https://www.albahari.com/nutshell/predicatebuilder.aspx) to support easy AND and OR combinations of your predicates from within your individual specifications, by all means do so. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should remove this paragraph. Or, update it and suggest using specification builder extensions.
// CustomerPredicates.cs | ||
// Predicates should take in an entity (and optionally, other arguments) and return a boolean | ||
// Optionally these can be extension methods, but that may reduce the ability to combine them consistently | ||
internal static class CustomerPredicates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should update this example with the one provided in the PR comments. I updated our sample too, so perhaps you can pick up the code from this commit 924a68c
No description provided.