Skip to content

Commit 62ca358

Browse files
authored
Ardalis/cleanup (#332)
* working on docs and cleaning up sample * add docs for evaluator
1 parent 0805a1e commit 62ca358

File tree

10 files changed

+90
-22
lines changed

10 files changed

+90
-22
lines changed

Specification/src/Ardalis.Specification/IReadRepositoryBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public interface IReadRepositoryBase<T> where T : class
1919
/// </summary>
2020
/// <typeparam name="TId">The type of primary key.</typeparam>
2121
/// <param name="id">The value of the primary key for the entity to be found.</param>
22+
/// <param name="cancellationToken"></param>
2223
/// <returns>
2324
/// A task that represents the asynchronous operation.
2425
/// The task result contains the <typeparamref name="T" />, or <see langword="null"/>.

Specification/src/Ardalis.Specification/IRepositoryBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public interface IRepositoryBase<T> : IReadRepositoryBase<T> where T : class
1818
/// Adds an entity in the database.
1919
/// </summary>
2020
/// <param name="entity">The entity to add.</param>
21+
/// <param name="cancellationToken"></param>
2122
/// <returns>
2223
/// A task that represents the asynchronous operation.
2324
/// The task result contains the <typeparamref name="T" />.
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
namespace Ardalis.Specification
1+
using System;
2+
3+
namespace Ardalis.Specification
24
{
35
/// <summary>
46
/// A marker interface for specifications that are meant to return a single entity. Used to constrain methods
57
/// that accept a Specification and return a single result rather than a collection of results.
68
/// </summary>
9+
[Obsolete("Use ISingleResultSpecification<T> instead. This interface will be removed in a future version of Ardalis.Specification.")]
710
public interface ISingleResultSpecification
811
{
912
}
@@ -12,7 +15,7 @@ public interface ISingleResultSpecification
1215
/// Encapsulates query logic for <typeparamref name="T"/>. It is meant to return a single result.
1316
/// </summary>
1417
/// <typeparam name="T">The type being queried against.</typeparam>
15-
public interface ISingleResultSpecification<T> : ISpecification<T>, ISingleResultSpecification
18+
public interface ISingleResultSpecification<T> : ISpecification<T>//, ISingleResultSpecification
1619
{
1720
}
1821

@@ -22,7 +25,7 @@ public interface ISingleResultSpecification<T> : ISpecification<T>, ISingleResul
2225
/// </summary>
2326
/// <typeparam name="T">The type being queried against.</typeparam>
2427
/// <typeparam name="TResult">The type of the result.</typeparam>
25-
public interface ISingleResultSpecification<T, TResult> : ISpecification<T, TResult>, ISingleResultSpecification
28+
public interface ISingleResultSpecification<T, TResult> : ISpecification<T, TResult>//, ISingleResultSpecification
2629
{
2730
}
2831
}

docs/extensions/extend-define-evaluators.md

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,59 @@ parent: Extensions
55
nav_order: 3
66
---
77

8-
How to extend or define your own evaluators
8+
How to extend or define your own evaluators.
9+
10+
## Evaluators
11+
12+
Evaluators are used within the specification to compose the query that will be executed. You can add your own evaluators to extend the behavior of the base Specification class.
13+
14+
Here is an example:
15+
16+
```csharp
17+
public class MyPartialEvaluator : IEvaluator
18+
{
19+
private MyPartialEvaluator () { }
20+
public static MyPartialEvaluator Instance { get; } = new MyPartialEvaluator();
21+
22+
public bool IsCriteriaEvaluator { get; } = true;
23+
24+
public IQueryable<T> GetQuery<T>(IQueryable<T> query, ISpecification<T> specification) where T : class
25+
{
26+
// Write your desired implementation
27+
28+
return query;
29+
}
30+
}
31+
32+
public class MySpecificationEvaluator : SpecificationEvaluator
33+
{
34+
public static MySpecificationEvaluator Instance { get; } = new MySpecificationEvaluator();
35+
36+
private MySpecificationEvaluator() : base()
37+
{
38+
Evaluators.Add(MyPartialEvaluator.Instance);
39+
}
40+
}
41+
```
42+
43+
To use the evaluator, you would pass it into your repository implementation's constructor:
44+
45+
```csharp
46+
public class Repository<T> : RepositoryBase<T>, IRepository<T> where T : class
47+
{
48+
public Repository(AppDbContext dbContext)
49+
: base(dbContext, MySpecificationEvaluator.Instance)
50+
{
51+
}
52+
}
53+
```
54+
55+
Of course you would also need to register the service in `Program.cs`:
56+
57+
```csharp
58+
builder.Services.AddScoped<ISpecificationEvaluator, MySpecificationEvaluator>();
59+
```
60+
61+
## References
62+
63+
- [Enabled by PR 328](https://github.com/ardalis/Specification/pull/328)

docs/extensions/extend-specification-builder.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ parent: Extensions
55
nav_order: 2
66
---
77

8-
98
# How to add extensions to the specification builder
109

1110
The specification builder from `Ardalis.Specification` is extensible by design. In fact, the methods you can use out of the box are implemented as extension methods themselves (check out the [source code](https://github.com/ardalis/Specification/blob/main/Specification/src/Ardalis.Specification/Builder/SpecificationBuilderExtensions.cs)). Your project might have requirements that cannot be satisfied by the existing toolset of course, or you might want to simplify repetitive code in several specification constructors. Whatever your case, enhancing the default builder is easy by creating your own extension methods.
@@ -20,7 +19,7 @@ Query.AsNoTracking()
2019
From here you can inspect the return type of the builder method you chained it to (`AsNoTracking`), and create an extension method on that interface (it doesn't need to be chained of course -- working on `Query` itself is also valid). This will most likely be `ISpecificationBuilder<T>`, but in some cases it's an inherited inteface. The example below illustrates how extension methods on inherited interfaces allow the builder to offer specific methods in specific contexts.
2120

2221

23-
## Example: Configure caching behaviour through specification builder extension method
22+
## Example: Configure caching behavior through specification builder extension method
2423

2524
In order to achieve this (note the `.WithTimeToLive` method):
2625

docs/usage/create-specifications.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ public class ItemByIdSpec : Specification<Item>
3939
}
4040
```
4141

42-
Finally: the Specification above should also implement the marker interface `ISingleResultSpecification`, which makes clear that this Specification will return only one result. Any "ById" Specification, and any other Specification intended to return only one result, should implement this interface to make clear that it returns a single result.
42+
Finally: the Specification above should also implement the marker interface `ISingleResultSpecification<T>`, which makes clear that this Specification will return only one result. Any "ById" Specification, and any other Specification intended to return only one result, should implement this interface to make clear that it returns a single result.
4343

4444
```csharp
45-
public class ItemByIdSpec : Specification<Item>, ISingleResultSpecification
45+
public class ItemByIdSpec : SingleResultSpecification<Item>
4646
{
4747
public ItemByIdSpec(int Id)
4848
{

sample/Ardalis.SampleApp.Core/Specifications/CustomerByNameSpec.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33

44
namespace Ardalis.SampleApp.Core.Specifications
55
{
6-
public class CustomerByNameSpec : Specification<Customer>, ISingleResultSpecification
6+
/// <summary>
7+
/// This specification expects customer names to be unique - change the base type if you want to support multiple results
8+
/// </summary>
9+
public class CustomerByNameSpec : SingleResultSpecification<Customer>
710
{
811
public CustomerByNameSpec(string name)
912
{

sample/Ardalis.SampleApp.Core/Specifications/CustomerByNameWithStoresSpec.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33

44
namespace Ardalis.SampleApp.Core.Specifications
55
{
6-
public class CustomerByNameWithStoresSpec : Specification<Customer>, ISingleResultSpecification
6+
/// <summary>
7+
/// This specification expects customer names to be unique - change the base type if you want to support multiple results
8+
/// </summary>
9+
public class CustomerByNameWithStoresSpec : SingleResultSpecification<Customer>
710
{
811
public CustomerByNameWithStoresSpec(string name)
912
{

sample/Ardalis.SampleApp.Core/Specifications/CustomerSpec.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
namespace Ardalis.SampleApp.Core.Specifications
66
{
7+
/// <summary>
8+
/// This specification expects 0 to many results
9+
/// </summary>
710
public class CustomerSpec : Specification<Customer>
811
{
912
public CustomerSpec(CustomerFilter filter)

sample/Ardalis.SampleApp.Infrastructure/Data/CachedCustomerRepository.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public Task<T> GetByIdAsync<TId>(TId id, CancellationToken cancellationToken = d
7676
}
7777

7878
/// <inheritdoc/>
79-
public Task<T> GetBySpecAsync(ISpecification<T> specification, CancellationToken cancellationToken = default)
79+
public Task<T?> FirstOrDefaultAsync(ISpecification<T> specification, CancellationToken cancellationToken = default)
8080
{
8181
if (specification.CacheEnabled)
8282
{
@@ -86,24 +86,18 @@ public Task<T> GetBySpecAsync(ISpecification<T> specification, CancellationToken
8686
{
8787
entry.SetOptions(_cacheOptions);
8888
_logger.LogWarning("Fetching source data for " + key);
89-
return _sourceRepository.GetBySpecAsync(specification, cancellationToken);
89+
return _sourceRepository.FirstOrDefaultAsync(specification, cancellationToken);
9090
});
9191
}
92-
return _sourceRepository.GetBySpecAsync(specification, cancellationToken);
92+
return _sourceRepository.FirstOrDefaultAsync(specification, cancellationToken);
9393
}
9494

9595
/// <inheritdoc/>
96-
public Task<TResult> GetBySpecAsync<TResult>(Specification.ISpecification<T, TResult> specification, CancellationToken cancellationToken = default)
96+
public Task<TResult> GetBySpecAsync<TResult>(ISpecification<T, TResult> specification, CancellationToken cancellationToken = default)
9797
{
9898
throw new NotImplementedException();
9999
}
100100

101-
/// <inheritdoc/>
102-
public virtual async Task<T?> FirstOrDefaultAsync(ISpecification<T> specification, CancellationToken cancellationToken = default)
103-
{
104-
return await _sourceRepository.FirstOrDefaultAsync(specification, cancellationToken);
105-
}
106-
107101
/// <inheritdoc/>
108102
public virtual async Task<TResult?> FirstOrDefaultAsync<TResult>(ISpecification<T, TResult> specification, CancellationToken cancellationToken = default)
109103
{
@@ -134,7 +128,7 @@ public Task<List<T>> ListAsync(CancellationToken cancellationToken = default)
134128
}
135129

136130
/// <inheritdoc/>
137-
public Task<List<T>> ListAsync(Specification.ISpecification<T> specification, CancellationToken cancellationToken = default)
131+
public Task<List<T>> ListAsync(ISpecification<T> specification, CancellationToken cancellationToken = default)
138132
{
139133
if (specification.CacheEnabled)
140134
{
@@ -151,7 +145,13 @@ public Task<List<T>> ListAsync(Specification.ISpecification<T> specification, Ca
151145
}
152146

153147
/// <inheritdoc/>
154-
public Task<List<TResult>> ListAsync<TResult>(Specification.ISpecification<T, TResult> specification, CancellationToken cancellationToken = default)
148+
public Task<List<TResult>> ListAsync<TResult>(ISpecification<T, TResult> specification, CancellationToken cancellationToken = default)
149+
{
150+
throw new NotImplementedException();
151+
}
152+
153+
/// <inheritdoc/>
154+
public Task<T> GetBySpecAsync(ISpecification<T> specification, CancellationToken cancellationToken = default)
155155
{
156156
throw new NotImplementedException();
157157
}

0 commit comments

Comments
 (0)