Skip to content

Commit 73693ac

Browse files
mustafaelshobakyMustafa.Elshobakyardalis
authored
Adding some xml comments (#168)
* Add xml comments for Specification classes * add xml comments to Specification Bulder extensions * Update Specification/src/Ardalis.Specification/Builder/SpecificationBuilderExtensions.cs Co-authored-by: Steve Smith <steve@kentsmiths.com> * Update Specification/src/Ardalis.Specification/Builder/SpecificationBuilderExtensions.cs Co-authored-by: Steve Smith <steve@kentsmiths.com> * Update Specification/src/Ardalis.Specification/ISpecification.cs Co-authored-by: Steve Smith <steve@kentsmiths.com> * Update Specification/src/Ardalis.Specification/ISpecification.cs Co-authored-by: Steve Smith <steve@kentsmiths.com> * Update Specification/src/Ardalis.Specification/Builder/SpecificationBuilderExtensions.cs Co-authored-by: Steve Smith <steve@kentsmiths.com> * Update Specification/src/Ardalis.Specification/ISingleResultSpecification.cs Co-authored-by: Steve Smith <steve@kentsmiths.com> Co-authored-by: Mustafa.Elshobaky <mustafa.elshobaky@integrant.com> Co-authored-by: Steve Smith <steve@kentsmiths.com>
1 parent 65c2dc7 commit 73693ac

File tree

5 files changed

+135
-28
lines changed

5 files changed

+135
-28
lines changed

Specification/src/Ardalis.Specification/Builder/SpecificationBuilderExtensions.cs

Lines changed: 94 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ namespace Ardalis.Specification
77
{
88
public static class SpecificationBuilderExtensions
99
{
10+
/// <summary>
11+
/// Specify a predicate that will be applied to the query
12+
/// </summary>
13+
/// <typeparam name="T"></typeparam>
14+
/// <param name="specificationBuilder"></param>
15+
/// <param name="criteria"></param>
1016
public static ISpecificationBuilder<T> Where<T>(
1117
this ISpecificationBuilder<T> specificationBuilder,
1218
Expression<Func<T, bool>> criteria)
@@ -16,6 +22,12 @@ public static ISpecificationBuilder<T> Where<T>(
1622
return specificationBuilder;
1723
}
1824

25+
/// <summary>
26+
/// Specify the query result will be ordered by <paramref name="orderExpression"/> in an ascending order
27+
/// </summary>
28+
/// <typeparam name="T"></typeparam>
29+
/// <param name="specificationBuilder"></param>
30+
/// <param name="orderExpression"></param>
1931
public static IOrderedSpecificationBuilder<T> OrderBy<T>(
2032
this ISpecificationBuilder<T> specificationBuilder,
2133
Expression<Func<T, object?>> orderExpression)
@@ -28,6 +40,12 @@ public static IOrderedSpecificationBuilder<T> OrderBy<T>(
2840
return orderedSpecificationBuilder;
2941
}
3042

43+
/// <summary>
44+
/// Specify the query result will be ordered by <paramref name="orderExpression"/> in a descending order
45+
/// </summary>
46+
/// <typeparam name="T"></typeparam>
47+
/// <param name="specificationBuilder"></param>
48+
/// <param name="orderExpression"></param>
3149
public static IOrderedSpecificationBuilder<T> OrderByDescending<T>(
3250
this ISpecificationBuilder<T> specificationBuilder,
3351
Expression<Func<T, object?>> orderExpression)
@@ -40,6 +58,15 @@ public static IOrderedSpecificationBuilder<T> OrderByDescending<T>(
4058
return orderedSpecificationBuilder;
4159
}
4260

61+
/// <summary>
62+
/// Specify an include expression.
63+
/// This information is utilized to build Include function in the query, which ORM tools like Entity Framework use
64+
/// to include related entities (via navigation properties) in the query result.
65+
/// </summary>
66+
/// <typeparam name="T"></typeparam>
67+
/// <typeparam name="TProperty"></typeparam>
68+
/// <param name="specificationBuilder"></param>
69+
/// <param name="includeExpression"></param>
4370
public static IIncludableSpecificationBuilder<T, TProperty> Include<T, TProperty>(
4471
this ISpecificationBuilder<T> specificationBuilder,
4572
Expression<Func<T, TProperty>> includeExpression) where T : class
@@ -53,6 +80,12 @@ public static IIncludableSpecificationBuilder<T, TProperty> Include<T, TProperty
5380
return includeBuilder;
5481
}
5582

83+
/// <summary>
84+
/// Specify a collection of navigation properties, as strings, to include in the query.
85+
/// </summary>
86+
/// <typeparam name="T"></typeparam>
87+
/// <param name="specificationBuilder"></param>
88+
/// <param name="includeString"></param>
5689
public static ISpecificationBuilder<T> Include<T>(
5790
this ISpecificationBuilder<T> specificationBuilder,
5891
string includeString) where T : class
@@ -61,7 +94,15 @@ public static ISpecificationBuilder<T> Include<T>(
6194
return specificationBuilder;
6295
}
6396

64-
97+
/// <summary>
98+
/// Specify a 'SQL LIKE' operations for search purposes
99+
/// </summary>
100+
/// <typeparam name="T"></typeparam>
101+
/// <param name="specificationBuilder"></param>
102+
/// <param name="selector">the property to apply the SQL LIKE against</param>
103+
/// <param name="searchTerm">the value to use for the SQL LIKE</param>
104+
/// <param name="searchGroup">the index used to group sets of Selectors and SearchTerms together</param>
105+
/// <returns></returns>
65106
public static ISpecificationBuilder<T> Search<T>(
66107
this ISpecificationBuilder<T> specificationBuilder,
67108
Expression<Func<T, string>> selector,
@@ -74,6 +115,9 @@ public static ISpecificationBuilder<T> Search<T>(
74115
return specificationBuilder;
75116
}
76117

118+
/// <summary>
119+
/// Specify the number of elements to return.
120+
/// </summary>
77121
public static ISpecificationBuilder<T> Take<T>(
78122
this ISpecificationBuilder<T> specificationBuilder,
79123
int take)
@@ -85,6 +129,12 @@ public static ISpecificationBuilder<T> Take<T>(
85129
return specificationBuilder;
86130
}
87131

132+
/// <summary>
133+
/// Specify the number of elements to skip before returning the remaining elements.
134+
/// </summary>
135+
/// <typeparam name="T"></typeparam>
136+
/// <param name="specificationBuilder"></param>
137+
/// <param name="skip">number of elements to skip</param>
88138
public static ISpecificationBuilder<T> Skip<T>(
89139
this ISpecificationBuilder<T> specificationBuilder,
90140
int skip)
@@ -108,24 +158,36 @@ public static ISpecificationBuilder<T> Paginate<T>(
108158
return specificationBuilder;
109159
}
110160

111-
public static ISpecificationBuilder<T> PostProcessingAction<T>(
112-
this ISpecificationBuilder<T> specificationBuilder,
113-
Func<IEnumerable<T>, IEnumerable<T>> predicate)
161+
/// <summary>
162+
/// Specify a transform function to apply to the <typeparamref name="T"/> element
163+
/// to produce another <typeparamref name="TResult"/> element.
164+
/// </summary>
165+
public static ISpecificationBuilder<T, TResult> Select<T, TResult>(
166+
this ISpecificationBuilder<T, TResult> specificationBuilder,
167+
Expression<Func<T, TResult>> selector)
114168
{
115-
specificationBuilder.Specification.PostProcessingAction = predicate;
169+
specificationBuilder.Specification.Selector = selector;
116170

117171
return specificationBuilder;
118172
}
119173

120-
public static ISpecificationBuilder<T, TResult> Select<T, TResult>(
121-
this ISpecificationBuilder<T, TResult> specificationBuilder,
122-
Expression<Func<T, TResult>> selector)
174+
/// <summary>
175+
/// Specify a transform function to apply to the result of the query
176+
/// and returns the same <typeparamref name="T"/> type
177+
/// </summary>
178+
public static ISpecificationBuilder<T> PostProcessingAction<T>(
179+
this ISpecificationBuilder<T> specificationBuilder,
180+
Func<IEnumerable<T>, IEnumerable<T>> predicate)
123181
{
124-
specificationBuilder.Specification.Selector = selector;
182+
specificationBuilder.Specification.PostProcessingAction = predicate;
125183

126184
return specificationBuilder;
127185
}
128186

187+
/// <summary>
188+
/// Specify a transform function to apply to the result of the query.
189+
/// and returns another <typeparamref name="TResult"/> type
190+
/// </summary>
129191
public static ISpecificationBuilder<T, TResult> PostProcessingAction<T, TResult>(
130192
this ISpecificationBuilder<T, TResult> specificationBuilder,
131193
Func<IEnumerable<TResult>, IEnumerable<TResult>> predicate)
@@ -158,6 +220,10 @@ public static ICacheSpecificationBuilder<T> EnableCache<T>(
158220
return cacheBuilder;
159221
}
160222

223+
/// <summary>
224+
/// If the entity instances are modified, this will not be detected
225+
/// by the change tracker.
226+
/// </summary>
161227
public static ISpecificationBuilder<T> AsNoTracking<T>(
162228
this ISpecificationBuilder<T> specificationBuilder) where T : class
163229
{
@@ -166,6 +232,15 @@ public static ISpecificationBuilder<T> AsNoTracking<T>(
166232
return specificationBuilder;
167233
}
168234

235+
/// <summary>
236+
/// The generated sql query will be split into multiple SQL queries
237+
/// </summary>
238+
/// <remarks>
239+
/// This feature was introduced in EF Core 5.0. It only works when using Include
240+
/// for more info: https://docs.microsoft.com/en-us/ef/core/querying/single-split-queries
241+
/// </remarks>
242+
/// <typeparam name="T"></typeparam>
243+
/// <param name="specificationBuilder"></param>
169244
public static ISpecificationBuilder<T> AsSplitQuery<T>(
170245
this ISpecificationBuilder<T> specificationBuilder) where T : class
171246
{
@@ -174,6 +249,16 @@ public static ISpecificationBuilder<T> AsSplitQuery<T>(
174249
return specificationBuilder;
175250
}
176251

252+
/// <summary>
253+
/// The query will then keep track of returned instances
254+
/// (without tracking them in the normal way)
255+
/// and ensure no duplicates are created in the query results
256+
/// </summary>
257+
/// <remarks>
258+
/// for more info: https://docs.microsoft.com/en-us/ef/core/change-tracking/identity-resolution#identity-resolution-and-queries
259+
/// </remarks>
260+
/// <typeparam name="T"></typeparam>
261+
/// <param name="specificationBuilder"></param>
177262
public static ISpecificationBuilder<T> AsNoTrackingWithIdentityResolution<T>(
178263
this ISpecificationBuilder<T> specificationBuilder) where T : class
179264
{

Specification/src/Ardalis.Specification/ISingleResultSpecification.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Text;
4-
5-
namespace Ardalis.Specification
1+
namespace Ardalis.Specification
62
{
3+
/// <summary>
4+
/// A marker interface for specifications that are meant to return a single entity. Used to constrain methods
5+
/// that accept a Specification and return a single result rather than a collection of results
6+
/// </summary>
77
public interface ISingleResultSpecification
88
{
99
}

Specification/src/Ardalis.Specification/ISingleResultSpecificationOfT.cs

Lines changed: 0 additions & 11 deletions
This file was deleted.

Specification/src/Ardalis.Specification/ISpecification.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public interface ISpecification<T, TResult> : ISpecification<T>
1616
/// The transform function to apply to the <typeparamref name="T"/> element.
1717
/// </summary>
1818
Expression<Func<T, TResult>>? Selector { get; }
19+
1920
/// <summary>
2021
/// The transform function to apply to the result of the query encapsulated by the <see cref="ISpecification{T, TResult}"/>.
2122
/// </summary>
@@ -34,22 +35,26 @@ public interface ISpecification<T>
3435
/// The collection of predicates to filter on.
3536
/// </summary>
3637
IEnumerable<Expression<Func<T, bool>>> WhereExpressions { get; }
38+
3739
/// <summary>
3840
/// The collections of functions used to determine the sorting (and subsequent sorting),
3941
/// to apply to the result of the query encapsulated by the <see cref="ISpecification{T}"/>.
4042
/// <para>KeySelector, a function to extract a key from an element.</para>
4143
/// <para>OrderType, whether to (subsequently) sort ascending or descending</para>
4244
/// </summary>
4345
IEnumerable<(Expression<Func<T, object>> KeySelector, OrderTypeEnum OrderType)> OrderExpressions { get; }
46+
4447
/// <summary>
4548
/// The collection of <see cref="IncludeExpressionInfo"/>s describing each include expression.
4649
/// This information is utilized to build Include/ThenInclude functions in the query.
4750
/// </summary>
4851
IEnumerable<IncludeExpressionInfo> IncludeExpressions { get; }
52+
4953
/// <summary>
5054
/// The collection of navigation properties, as strings, to include in the query.
5155
/// </summary>
5256
IEnumerable<string> IncludeStrings { get; }
57+
5358
/// <summary>
5459
/// The collection of 'SQL LIKE' operations, constructed by;
5560
/// <list type="bullet">
@@ -64,10 +69,12 @@ public interface ISpecification<T>
6469
/// The number of elements to return.
6570
/// </summary>
6671
int? Take { get; }
72+
6773
/// <summary>
6874
/// The number of elements to skip before returning the remaining elements.
6975
/// </summary>
7076
int? Skip { get; }
77+
7178
[Obsolete]
7279
bool IsPagingEnabled { get; }
7380

@@ -80,6 +87,7 @@ public interface ISpecification<T>
8087
/// Return whether or not the results should be cached.
8188
/// </summary>
8289
bool CacheEnabled { get; }
90+
8391
/// <summary>
8492
/// The identifier to use to store and retrieve results from the cache.
8593
/// </summary>
@@ -91,9 +99,32 @@ public interface ISpecification<T>
9199
/// by the change tracker.
92100
/// </summary>
93101
bool AsNoTracking { get; }
102+
103+
/// <summary>
104+
/// Returns whether or not the generated sql query should be split into multiple SQL queries
105+
/// </summary>
106+
/// <remarks>
107+
/// This feature was introduced in EF Core 5.0. It only works when using Include
108+
/// for more info: https://docs.microsoft.com/en-us/ef/core/querying/single-split-queries
109+
/// </remarks>
94110
bool AsSplitQuery { get; }
111+
112+
/// <summary>
113+
/// Returns whether or not the query will then keep track of returned instances
114+
/// (without tracking them in the normal way)
115+
/// and ensure no duplicates are created in the query results
116+
/// </summary>
117+
/// <remarks>
118+
/// for more info: https://docs.microsoft.com/en-us/ef/core/change-tracking/identity-resolution#identity-resolution-and-queries
119+
/// </remarks>
95120
bool AsNoTrackingWithIdentityResolution { get; }
96121

122+
/// <summary>
123+
/// Applies the query defined within the specification to the given objects.
124+
/// This is specially helpful when unit testing specification classes
125+
/// </summary>
126+
/// <param name="entities">the list of entities to which the specification will be applied</param>
127+
/// <returns></returns>
97128
IEnumerable<T> Evaluate(IEnumerable<T> entities);
98129
}
99130
}

Specification/src/Ardalis.Specification/Specification.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq.Expressions;
4-
using System.Text;
54

65
namespace Ardalis.Specification
76
{
@@ -49,6 +48,7 @@ protected Specification(IInMemorySpecificationEvaluator inMemorySpecificationEva
4948
this.Query = new SpecificationBuilder<T>(this);
5049
}
5150

51+
/// <inheritdoc/>
5252
public virtual IEnumerable<T> Evaluate(IEnumerable<T> entities)
5353
{
5454
return Evaluator.Evaluate(entities, this);
@@ -70,7 +70,6 @@ public virtual IEnumerable<T> Evaluate(IEnumerable<T> entities)
7070
public IEnumerable<(Expression<Func<T, string>> Selector, string SearchTerm, int SearchGroup)> SearchCriterias { get; } =
7171
new List<(Expression<Func<T, string>> Selector, string SearchTerm, int SearchGroup)>();
7272

73-
7473
/// <inheritdoc/>
7574
public int? Take { get; internal set; } = null;
7675

@@ -80,7 +79,6 @@ public virtual IEnumerable<T> Evaluate(IEnumerable<T> entities)
8079
/// <inheritdoc/>
8180
public bool IsPagingEnabled { get; internal set; } = false;
8281

83-
8482
/// <inheritdoc/>
8583
public Func<IEnumerable<T>, IEnumerable<T>>? PostProcessingAction { get; internal set; } = null;
8684

@@ -92,7 +90,11 @@ public virtual IEnumerable<T> Evaluate(IEnumerable<T> entities)
9290

9391
/// <inheritdoc/>
9492
public bool AsNoTracking { get; internal set; } = false;
93+
94+
/// <inheritdoc/>
9595
public bool AsSplitQuery { get; internal set; } = false;
96+
97+
/// <inheritdoc/>
9698
public bool AsNoTrackingWithIdentityResolution { get; internal set; } = false;
9799
}
98100
}

0 commit comments

Comments
 (0)