Skip to content

Commit e369fa2

Browse files
authored
docs: add XML docs for repository types (#194)
LLMs burn unnecessary tokens per-session trying to understand these types. Document the functionality, guarantees and exceptions to avoid this.
1 parent 81ac113 commit e369fa2

6 files changed

Lines changed: 258 additions & 7 deletions

File tree

src/OneBeyond.Studio.Application.SharedKernel/Repositories/IRORepository.cs

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,73 +3,169 @@
33
using System.Linq.Expressions;
44
using System.Threading;
55
using System.Threading.Tasks;
6+
using OneBeyond.Studio.Application.SharedKernel.Repositories.Exceptions;
67
using OneBeyond.Studio.Application.SharedKernel.Specifications;
78
using OneBeyond.Studio.Domain.SharedKernel.Entities;
89
using OneBeyond.Studio.Domain.SharedKernel.Specifications;
910

1011
namespace OneBeyond.Studio.Application.SharedKernel.Repositories;
1112

1213
/// <summary>
13-
/// Represents a repository with read-only operations.
14+
/// Read-only repository exposing query operations over <typeparamref name="TEntity"/>.
15+
/// Results are detached.
1416
/// </summary>
17+
/// <typeparam name="TEntity">Type of the queried entity.</typeparam>
1518
public interface IRORepository<TEntity>
1619
{
20+
/// <summary>
21+
/// Returns the entities matching <paramref name="filter"/>, or all entities when it is <see langword="null"/>.
22+
/// </summary>
23+
/// <param name="filter">Predicate to match, or <see langword="null"/> to match every entity.</param>
24+
/// <param name="includes">Related object graph to load alongside each entity, or <see langword="null"/> for none.</param>
25+
/// <param name="paging">Slice of the result set to return, or <see langword="null"/> for all matches.</param>
26+
/// <param name="sortings">Ordering to apply.</param>
27+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
1728
Task<IReadOnlyCollection<TEntity>> ListAsync(
1829
Expression<Func<TEntity, bool>>? filter = default,
1930
Includes<TEntity>? includes = default,
2031
Paging? paging = default,
2132
IReadOnlyCollection<Sorting<TEntity>>? sortings = default,
2233
CancellationToken cancellationToken = default);
2334

35+
/// <summary>
36+
/// Projects the entities matching <paramref name="preFilter"/> onto <typeparamref name="TResultDto"/>,
37+
/// then returns those matching <paramref name="filter"/>.
38+
/// </summary>
39+
/// <remarks>
40+
/// <paramref name="preFilter"/> is applied in entity space before projection; <paramref name="filter"/>
41+
/// is applied in result space after it. The projection is resolved from the registered mapping for
42+
/// <typeparamref name="TResultDto"/>.
43+
/// </remarks>
44+
/// <typeparam name="TResultDto">Type each entity is projected onto.</typeparam>
45+
/// <param name="preFilter">Predicate applied to entities before projection, or <see langword="null"/> to match all.</param>
46+
/// <param name="filter">Predicate applied to projected results, or <see langword="null"/> to match all.</param>
47+
/// <param name="paging">Slice of the result set to return, or <see langword="null"/> for all matches.</param>
48+
/// <param name="sortings">Ordering to apply.</param>
49+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
2450
Task<IReadOnlyCollection<TResultDto>> ListAsync<TResultDto>(
2551
Expression<Func<TEntity, bool>>? preFilter,
2652
Expression<Func<TResultDto, bool>>? filter = default,
2753
Paging? paging = default,
2854
IReadOnlyCollection<Sorting<TResultDto>>? sortings = default,
2955
CancellationToken cancellationToken = default);
3056

57+
/// <summary>
58+
/// Returns the entities matching <paramref name="filter"/> projected onto <typeparamref name="TResultDto"/>
59+
/// via <paramref name="projection"/>.
60+
/// </summary>
61+
/// <typeparam name="TResultDto">Type each entity is projected onto.</typeparam>
62+
/// <param name="projection">Expression mapping an entity to a result. Translated to the query, not run in memory.</param>
63+
/// <param name="filter">Predicate applied to entities before projection, or <see langword="null"/> to match all.</param>
64+
/// <param name="paging">Slice of the result set to return, or <see langword="null"/> for all matches.</param>
65+
/// <param name="sortings">Ordering applied to entities before projection.</param>
66+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
3167
Task<IReadOnlyCollection<TResultDto>> ListAsync<TResultDto>(
3268
Expression<Func<TEntity, TResultDto>> projection,
3369
Expression<Func<TEntity, bool>>? filter = default,
3470
Paging? paging = default,
3571
IReadOnlyCollection<Sorting<TEntity>>? sortings = default,
3672
CancellationToken cancellationToken = default);
3773

74+
/// <summary>
75+
/// Counts the entities matching <paramref name="filter"/>, or all entities when it is <see langword="null"/>.
76+
/// </summary>
77+
/// <param name="filter">Predicate to match, or <see langword="null"/> to count every entity.</param>
78+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
3879
Task<int> CountAsync(
3980
Expression<Func<TEntity, bool>>? filter = default,
4081
CancellationToken cancellationToken = default);
4182

83+
/// <summary>
84+
/// Counts the entities matching <paramref name="preFilter"/> whose <typeparamref name="TResultDto"/>
85+
/// projection matches <paramref name="filter"/>.
86+
/// </summary>
87+
/// <typeparam name="TResultDto">Type each entity is projected onto.</typeparam>
88+
/// <param name="preFilter">Predicate applied to entities before projection, or <see langword="null"/> to match all.</param>
89+
/// <param name="filter">Predicate applied to projected results, or <see langword="null"/> to match all.</param>
90+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
4291
Task<int> CountAsync<TResultDto>(
4392
Expression<Func<TEntity, bool>>? preFilter,
4493
Expression<Func<TResultDto, bool>>? filter = default,
4594
CancellationToken cancellationToken = default);
4695

96+
/// <summary>
97+
/// Determines whether any entity matches <paramref name="filter"/>, or whether any entity exists
98+
/// when it is <see langword="null"/>.
99+
/// </summary>
100+
/// <param name="filter">Predicate to match, or <see langword="null"/> to match every entity.</param>
101+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
47102
Task<bool> AnyAsync(
48103
Expression<Func<TEntity, bool>>? filter = default,
49104
CancellationToken cancellationToken = default);
50105

106+
/// <summary>
107+
/// Determines whether any entity matching <paramref name="preFilter"/> has a <typeparamref name="TResultDto"/>
108+
/// projection matching <paramref name="filter"/>.
109+
/// </summary>
110+
/// <typeparam name="TResultDto">Type each entity is projected onto.</typeparam>
111+
/// <param name="preFilter">Predicate applied to entities before projection, or <see langword="null"/> to match all.</param>
112+
/// <param name="filter">Predicate applied to projected results, or <see langword="null"/> to match all.</param>
113+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
51114
Task<bool> AnyAsync<TResultDto>(
52115
Expression<Func<TEntity, bool>>? preFilter,
53116
Expression<Func<TResultDto, bool>>? filter = default,
54117
CancellationToken cancellationToken = default);
55118
}
56119

57120
/// <summary>
58-
/// Represents a repository with read-only operations.
121+
/// Read-only repository that adds identifier-based lookups for an entity keyed by
122+
/// <typeparamref name="TEntityId"/>.
59123
/// </summary>
124+
/// <typeparam name="TEntity">Type of the queried entity.</typeparam>
125+
/// <typeparam name="TEntityId">Type of the entity identifier.</typeparam>
60126
public interface IRORepository<TEntity, TEntityId> : IRORepository<TEntity>
61127
where TEntity : DomainEntity<TEntityId>
62128
where TEntityId : notnull
63129
{
130+
/// <summary>
131+
/// Returns the entity identified by <paramref name="entityId"/>.
132+
/// </summary>
133+
/// <param name="entityId">Identifier of the entity to load.</param>
134+
/// <param name="includes">Related object graph to load alongside the entity, or <see langword="null"/> for none.</param>
135+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
136+
/// <returns>The matching entity; never <see langword="null"/>.</returns>
137+
/// <exception cref="EntityNotFoundException">No entity with <paramref name="entityId"/> exists.</exception>
138+
/// <exception cref="EntityAccessDeniedException">The entity exists but the active read policy denies access to it.</exception>
64139
Task<TEntity> GetByIdAsync(
65140
TEntityId entityId,
66141
Includes<TEntity>? includes,
67142
CancellationToken cancellationToken);
68143

144+
/// <summary>
145+
/// Returns the entity identified by <paramref name="entityId"/> projected onto <typeparamref name="TResultDto"/>
146+
/// using the registered mapping.
147+
/// </summary>
148+
/// <typeparam name="TResultDto">Type the entity is projected onto.</typeparam>
149+
/// <param name="entityId">Identifier of the entity to load.</param>
150+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
151+
/// <returns>The projected result; never <see langword="null"/>.</returns>
152+
/// <exception cref="EntityNotFoundException">No entity with <paramref name="entityId"/> exists.</exception>
153+
/// <exception cref="EntityAccessDeniedException">The entity exists but the active read policy denies access to it.</exception>
69154
Task<TResultDto> GetByIdAsync<TResultDto>(
70155
TEntityId entityId,
71156
CancellationToken cancellationToken);
72157

158+
/// <summary>
159+
/// Returns the entity identified by <paramref name="entityId"/> projected onto <typeparamref name="TResultDto"/>
160+
/// via <paramref name="projection"/>.
161+
/// </summary>
162+
/// <typeparam name="TResultDto">Type the entity is projected onto.</typeparam>
163+
/// <param name="entityId">Identifier of the entity to load.</param>
164+
/// <param name="projection">Expression mapping the entity to a result. Translated to the query, not run in memory.</param>
165+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
166+
/// <returns>The projected result; never <see langword="null"/>.</returns>
167+
/// <exception cref="EntityNotFoundException">No entity with <paramref name="entityId"/> exists.</exception>
168+
/// <exception cref="EntityAccessDeniedException">The entity exists but the active read policy denies access to it.</exception>
73169
Task<TResultDto> GetByIdAsync<TResultDto>(
74170
TEntityId entityId,
75171
Expression<Func<TEntity, TResultDto>> projection,

src/OneBeyond.Studio.Application.SharedKernel/Repositories/IRORepositoryExtensions.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,29 @@
44
using System.Threading;
55
using System.Threading.Tasks;
66
using EnsureThat;
7+
using OneBeyond.Studio.Application.SharedKernel.Repositories.Exceptions;
78
using OneBeyond.Studio.Domain.SharedKernel.Entities;
89
using OneBeyond.Studio.Domain.SharedKernel.Specifications;
910

1011
namespace OneBeyond.Studio.Application.SharedKernel.Repositories;
1112

13+
/// <summary>
14+
/// Convenience overloads for <see cref="IRORepository{TEntity, TEntityId}"/>.
15+
/// </summary>
1216
public static class IRORepositoryExtensions
1317
{
18+
/// <summary>
19+
/// Returns the entity identified by <paramref name="entityId"/>.
20+
/// </summary>
21+
/// <typeparam name="TEntity">Type of the queried entity.</typeparam>
22+
/// <typeparam name="TEntityId">Type of the entity identifier.</typeparam>
23+
/// <param name="roRepository">Repository to query.</param>
24+
/// <param name="entityId">Identifier of the entity to load.</param>
25+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
26+
/// <returns>The matching entity; never <see langword="null"/>.</returns>
27+
/// <exception cref="ArgumentNullException"><paramref name="roRepository"/> is <see langword="null"/>.</exception>
28+
/// <exception cref="EntityNotFoundException">No entity with <paramref name="entityId"/> exists.</exception>
29+
/// <exception cref="EntityAccessDeniedException">The entity exists but the active read policy denies access to it.</exception>
1430
public static Task<TEntity> GetByIdAsync<TEntity, TEntityId>(
1531
this IRORepository<TEntity, TEntityId> roRepository,
1632
TEntityId entityId,
@@ -26,6 +42,19 @@ public static Task<TEntity> GetByIdAsync<TEntity, TEntityId>(
2642
cancellationToken);
2743
}
2844

45+
/// <summary>
46+
/// Projects every entity onto <typeparamref name="TResultDto"/> and returns those matching
47+
/// <paramref name="filter"/>.
48+
/// </summary>
49+
/// <typeparam name="TEntity">Type of the queried entity.</typeparam>
50+
/// <typeparam name="TEntityId">Type of the entity identifier.</typeparam>
51+
/// <typeparam name="TResultDto">Type each entity is projected onto.</typeparam>
52+
/// <param name="roRepository">Repository to query.</param>
53+
/// <param name="filter">Predicate applied to projected results, or <see langword="null"/> to match all.</param>
54+
/// <param name="paging">Slice of the result set to return, or <see langword="null"/> for all matches.</param>
55+
/// <param name="sortings">Ordering to apply.</param>
56+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
57+
/// <exception cref="ArgumentNullException"><paramref name="roRepository"/> is <see langword="null"/>.</exception>
2958
public static Task<IReadOnlyCollection<TResultDto>> ListAsync<TEntity, TEntityId, TResultDto>(
3059
this IRORepository<TEntity, TEntityId> roRepository,
3160
Expression<Func<TResultDto, bool>>? filter = default,
@@ -45,6 +74,16 @@ public static Task<IReadOnlyCollection<TResultDto>> ListAsync<TEntity, TEntityId
4574
cancellationToken);
4675
}
4776

77+
/// <summary>
78+
/// Counts the entities whose <typeparamref name="TResultDto"/> projection matches <paramref name="filter"/>.
79+
/// </summary>
80+
/// <typeparam name="TEntity">Type of the queried entity.</typeparam>
81+
/// <typeparam name="TEntityId">Type of the entity identifier.</typeparam>
82+
/// <typeparam name="TResultDto">Type each entity is projected onto.</typeparam>
83+
/// <param name="roRepository">Repository to query.</param>
84+
/// <param name="filter">Predicate applied to projected results, or <see langword="null"/> to count all.</param>
85+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
86+
/// <exception cref="ArgumentNullException"><paramref name="roRepository"/> is <see langword="null"/>.</exception>
4887
public static Task<int> CountAsync<TEntity, TEntityId, TResultDto>(
4988
this IRORepository<TEntity, TEntityId> roRepository,
5089
Expression<Func<TResultDto, bool>>? filter = default,
@@ -60,6 +99,17 @@ public static Task<int> CountAsync<TEntity, TEntityId, TResultDto>(
6099
cancellationToken);
61100
}
62101

102+
/// <summary>
103+
/// Determines whether any entity has a <typeparamref name="TResultDto"/> projection matching
104+
/// <paramref name="filter"/>.
105+
/// </summary>
106+
/// <typeparam name="TEntity">Type of the queried entity.</typeparam>
107+
/// <typeparam name="TEntityId">Type of the entity identifier.</typeparam>
108+
/// <typeparam name="TResultDto">Type each entity is projected onto.</typeparam>
109+
/// <param name="roRepository">Repository to query.</param>
110+
/// <param name="filter">Predicate applied to projected results, or <see langword="null"/> to match all.</param>
111+
/// <param name="cancellationToken">Token used to cancel the operation.</param>
112+
/// <exception cref="ArgumentNullException"><paramref name="roRepository"/> is <see langword="null"/>.</exception>
63113
public static Task<bool> AnyAsync<TEntity, TEntityId, TResultDto>(
64114
this IRORepository<TEntity, TEntityId> roRepository,
65115
Expression<Func<TResultDto, bool>>? filter = default,

0 commit comments

Comments
 (0)