Skip to content

Commit 56c1370

Browse files
committed
Enhance XML docs for handlers and behaviors
1 parent 2085bb7 commit 56c1370

12 files changed

Lines changed: 207 additions & 56 deletions

Directory.Packages.props

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
<NoWarn>$(NoWarn);NU1507</NoWarn>
66
</PropertyGroup>
77
<ItemGroup>
8-
<PackageVersion Include="Aspire.Hosting.AppHost" Version="13.3.0" />
98
<PackageVersion Include="AspNetCore.SecurityKey" Version="4.3.0" />
109
<PackageVersion Include="AssemblyMetadata.Generators" Version="2.2.0" />
1110
<PackageVersion Include="AutoMapper" Version="[14.0.0]" />
@@ -83,4 +82,4 @@
8382
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
8483
<PackageVersion Update="Microsoft.EntityFrameworkCore" Version="9.0.15" />
8584
</ItemGroup>
86-
</Project>
85+
</Project>

src/Arbiter.CommandQuery.EntityFramework/Handlers/EntityCreateCommandHandler.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@
99
namespace Arbiter.CommandQuery.EntityFramework.Handlers;
1010

1111
/// <summary>
12-
/// A handler for a request that creates an entity in the specified <see cref="DbContext"/>.
12+
/// A handler that processes an <see cref="EntityCreateCommand{TCreateModel, TReadModel}"/> by mapping the
13+
/// create model to a new <typeparamref name="TEntity"/>, applying audit metadata, persisting the entity
14+
/// via the specified <typeparamref name="TContext"/>, and returning the created entity as a <typeparamref name="TReadModel"/>.
1315
/// </summary>
14-
/// <inheritdoc/>
16+
/// <typeparam name="TContext">The <see cref="DbContext"/> used to persist the entity.</typeparam>
17+
/// <typeparam name="TEntity">The entity type being created. Must implement <see cref="IHaveIdentifier{TKey}"/> and have a parameterless constructor.</typeparam>
18+
/// <typeparam name="TKey">The type of the entity's primary key.</typeparam>
19+
/// <typeparam name="TCreateModel">The input model type containing the data for the new entity.</typeparam>
20+
/// <typeparam name="TReadModel">The output model type returned after the entity is created.</typeparam>
1521
public class EntityCreateCommandHandler<TContext, TEntity, TKey, TCreateModel, TReadModel>
1622
: EntityDataContextHandlerBase<TContext, TEntity, TKey, TReadModel, EntityCreateCommand<TCreateModel, TReadModel>, TReadModel>
1723
where TContext : DbContext
@@ -20,13 +26,22 @@ public class EntityCreateCommandHandler<TContext, TEntity, TKey, TCreateModel, T
2026
/// <summary>
2127
/// Initializes a new instance of the <see cref="EntityCreateCommandHandler{TContext, TEntity, TKey, TCreateModel, TReadModel}"/> class.
2228
/// </summary>
23-
/// <inheritdoc/>
29+
/// <param name="loggerFactory">The logger factory used to create an <see cref="ILogger"/> for this handler.</param>
30+
/// <param name="dataContext">The <typeparamref name="TContext"/> used to persist the entity.</param>
31+
/// <param name="mapper">The mapper used to convert between <typeparamref name="TCreateModel"/> and <typeparamref name="TEntity"/>, and to project to <typeparamref name="TReadModel"/>.</param>
32+
/// <param name="pipeline">An optional query pipeline applied when reading back the created entity.</param>
2433
public EntityCreateCommandHandler(ILoggerFactory loggerFactory, TContext dataContext, IMapper mapper, IQueryPipeline? pipeline = null)
2534
: base(loggerFactory, dataContext, mapper, pipeline)
2635
{
2736
}
2837

2938
/// <inheritdoc/>
39+
/// <remarks>
40+
/// Maps <paramref name="request"/>.<c>Model</c> to a new <typeparamref name="TEntity"/>, then sets
41+
/// <see cref="ITrackCreated"/> and <see cref="ITrackUpdated"/> audit fields when supported.
42+
/// The entity is added to the <see cref="DbSet{TEntity}"/> and saved. The newly created entity
43+
/// is then read back and projected to <typeparamref name="TReadModel"/> before being returned.
44+
/// </remarks>
3045
protected override async ValueTask<TReadModel?> Process(
3146
EntityCreateCommand<TCreateModel, TReadModel> request,
3247
CancellationToken cancellationToken = default)

src/Arbiter.CommandQuery.EntityFramework/Handlers/EntityDataContextHandlerBase.cs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,26 @@ public abstract class EntityDataContextHandlerBase<TContext, TEntity, TKey, TRea
3939
/// <summary>
4040
/// Initializes a new instance of the <see cref="EntityDataContextHandlerBase{TContext, TEntity, TKey, TReadModel, TRequest, TResponse}"/> class.
4141
/// </summary>
42-
/// <inheritdoc/>
42+
/// <param name="loggerFactory">The logger factory used to create an <see cref="ILogger"/> for this handler.</param>
43+
/// <param name="dataContext">The <typeparamref name="TContext"/> used to access the data store.</param>
44+
/// <param name="mapper">The <see cref="IMapper"/> used to map between entity and model types.</param>
45+
/// <param name="pipeline">An optional <see cref="IQueryPipeline"/> applied to queries executed by this handler.</param>
46+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="loggerFactory"/>, <paramref name="dataContext"/>, or <paramref name="mapper"/> is <see langword="null"/>.</exception>
4347
protected EntityDataContextHandlerBase(ILoggerFactory loggerFactory, TContext dataContext, IMapper mapper, IQueryPipeline? pipeline = null)
4448
: base(loggerFactory, dataContext, mapper, pipeline)
4549
{
4650
}
4751

4852
/// <summary>
49-
/// Reads the entity from the data context.
53+
/// Reads the <typeparamref name="TEntity"/> identified by <paramref name="key"/> from the data context
54+
/// and projects it to <typeparamref name="TReadModel"/>.
5055
/// </summary>
51-
/// <param name="key">The entity key to read</param>
52-
/// <param name="pipelineName">The optional pipeline name to apply</param>
53-
/// <param name="principal">The optional claims principal</param>
54-
/// <param name="cancellationToken">The cancellation token</param>
55-
/// <returns>Awaitable task returning the <typeparamref name="TReadModel"/></returns>
56-
/// <exception cref="ArgumentNullException">Thrown when <paramref name="key"/> is null</exception>
56+
/// <param name="key">The primary key of the entity to read.</param>
57+
/// <param name="pipelineName">The optional name of the query pipeline to apply when querying the entity.</param>
58+
/// <param name="principal">The optional <see cref="ClaimsPrincipal"/> used by the query pipeline for authorization or filtering.</param>
59+
/// <param name="cancellationToken">The cancellation token.</param>
60+
/// <returns>The <typeparamref name="TReadModel"/> for the entity, or <see langword="null"/> if no matching entity is found.</returns>
61+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="key"/> is <see langword="null"/>.</exception>
5762
protected virtual async ValueTask<TReadModel?> Read(
5863
[NotNull] TKey key,
5964
string? pipelineName = null,

src/Arbiter.CommandQuery.EntityFramework/Handlers/EntityDeleteCommandHandler.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@
99
namespace Arbiter.CommandQuery.EntityFramework.Handlers;
1010

1111
/// <summary>
12-
/// A handler for a request that deletes an entity in the specified <see cref="DbContext"/>.
12+
/// A handler that processes an <see cref="EntityDeleteCommand{TKey, TReadModel}"/> by locating the
13+
/// <typeparamref name="TEntity"/> in the specified <typeparamref name="TContext"/>, reading it back as a
14+
/// <typeparamref name="TReadModel"/>, then performing a soft delete (via <see cref="ITrackDeleted"/>) or a
15+
/// hard delete, and finally saving changes.
1316
/// </summary>
14-
/// <inheritdoc/>
17+
/// <typeparam name="TContext">The <see cref="DbContext"/> used to access the data store.</typeparam>
18+
/// <typeparam name="TEntity">The entity type being deleted. Must implement <see cref="IHaveIdentifier{TKey}"/> and have a parameterless constructor.</typeparam>
19+
/// <typeparam name="TKey">The type of the entity's primary key.</typeparam>
20+
/// <typeparam name="TReadModel">The output model type returned after the entity is deleted.</typeparam>
1521
public class EntityDeleteCommandHandler<TContext, TEntity, TKey, TReadModel>
1622
: EntityDataContextHandlerBase<TContext, TEntity, TKey, TReadModel, EntityDeleteCommand<TKey, TReadModel>, TReadModel>
1723
where TContext : DbContext
@@ -20,13 +26,34 @@ public class EntityDeleteCommandHandler<TContext, TEntity, TKey, TReadModel>
2026
/// <summary>
2127
/// Initializes a new instance of the <see cref="EntityDeleteCommandHandler{TContext, TEntity, TKey, TReadModel}"/> class.
2228
/// </summary>
23-
/// <inheritdoc/>
29+
/// <param name="loggerFactory">The logger factory used to create an <see cref="ILogger"/> for this handler.</param>
30+
/// <param name="dataContext">The <typeparamref name="TContext"/> used to access the data store.</param>
31+
/// <param name="mapper">The <see cref="IMapper"/> used to project <typeparamref name="TEntity"/> to <typeparamref name="TReadModel"/>.</param>
32+
/// <param name="pipeline">An optional <see cref="IQueryPipeline"/> applied to queries executed by this handler.</param>
33+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="loggerFactory"/>, <paramref name="dataContext"/>, or <paramref name="mapper"/> is <see langword="null"/>.</exception>
2434
public EntityDeleteCommandHandler(ILoggerFactory loggerFactory, TContext dataContext, IMapper mapper, IQueryPipeline? pipeline = null)
2535
: base(loggerFactory, dataContext, mapper, pipeline)
2636
{
2737
}
2838

2939
/// <inheritdoc/>
40+
/// <remarks>
41+
/// <para>
42+
/// Queries the <see cref="DbSet{TEntity}"/> for the entity matching <c>request.Id</c>, applying any
43+
/// optional query pipeline modifiers. If no entity is found, returns <see langword="null"/>.
44+
/// </para>
45+
/// <para>
46+
/// Before deletion, the entity is read back and projected to <typeparamref name="TReadModel"/> so the
47+
/// caller receives the state of the entity prior to deletion.
48+
/// </para>
49+
/// <para>
50+
/// The delete strategy is determined by the entity's implemented interfaces:
51+
/// <list type="bullet">
52+
/// <item><description>If the entity implements <see cref="ITrackDeleted"/>, a soft delete is performed by setting <c>IsDeleted = true</c>.</description></item>
53+
/// <item><description>Otherwise, the entity is hard-deleted via <see cref="DbSet{TEntity}.Remove"/>. If the entity also implements <see cref="ITrackHistory"/> and <see cref="ITrackUpdated"/>, update metadata is saved first to preserve history.</description></item>
54+
/// </list>
55+
/// </para>
56+
/// </remarks>
3057
protected override async ValueTask<TReadModel?> Process(
3158
EntityDeleteCommand<TKey, TReadModel> request,
3259
CancellationToken cancellationToken = default)

src/Arbiter.CommandQuery.EntityFramework/Handlers/EntityIdentifierQueryHandler.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@
99
namespace Arbiter.CommandQuery.EntityFramework.Handlers;
1010

1111
/// <summary>
12-
/// A handler for a request that reads an entity in the specified <see cref="DbContext"/>.
12+
/// A handler that processes an <see cref="EntityIdentifierQuery{TKey, TReadModel}"/> by reading a single
13+
/// <typeparamref name="TEntity"/> by its primary key from the specified <typeparamref name="TContext"/>
14+
/// and projecting it to a <typeparamref name="TReadModel"/>.
1315
/// </summary>
14-
/// <inheritdoc/>
16+
/// <typeparam name="TContext">The <see cref="DbContext"/> used to access the data store.</typeparam>
17+
/// <typeparam name="TEntity">The entity type being queried. Must implement <see cref="IHaveIdentifier{TKey}"/> and have a parameterless constructor.</typeparam>
18+
/// <typeparam name="TKey">The type of the entity's primary key.</typeparam>
19+
/// <typeparam name="TReadModel">The output model type returned after the entity is read.</typeparam>
1520
public class EntityIdentifierQueryHandler<TContext, TEntity, TKey, TReadModel>
1621
: EntityDataContextHandlerBase<TContext, TEntity, TKey, TReadModel, EntityIdentifierQuery<TKey, TReadModel>, TReadModel>
1722
where TContext : DbContext
@@ -21,13 +26,22 @@ public class EntityIdentifierQueryHandler<TContext, TEntity, TKey, TReadModel>
2126
/// <summary>
2227
/// Initializes a new instance of the <see cref="EntityIdentifierQueryHandler{TContext, TEntity, TKey, TReadModel}"/> class.
2328
/// </summary>
24-
/// <inheritdoc/>
29+
/// <param name="loggerFactory">The logger factory used to create an <see cref="ILogger"/> for this handler.</param>
30+
/// <param name="dataContext">The <typeparamref name="TContext"/> used to access the data store.</param>
31+
/// <param name="mapper">The <see cref="IMapper"/> used to project <typeparamref name="TEntity"/> to <typeparamref name="TReadModel"/>.</param>
32+
/// <param name="pipeline">An optional <see cref="IQueryPipeline"/> applied to queries executed by this handler.</param>
33+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="loggerFactory"/>, <paramref name="dataContext"/>, or <paramref name="mapper"/> is <see langword="null"/>.</exception>
2534
public EntityIdentifierQueryHandler(ILoggerFactory loggerFactory, TContext dataContext, IMapper mapper, IQueryPipeline? pipeline = null)
2635
: base(loggerFactory, dataContext, mapper, pipeline)
2736
{
2837
}
2938

3039
/// <inheritdoc/>
40+
/// <remarks>
41+
/// Delegates to <see cref="EntityDataContextHandlerBase{TContext, TEntity, TKey, TReadModel, TRequest, TResponse}.Read"/>
42+
/// using <c>request.Id</c>, <c>request.FilterName</c>, and <c>request.Principal</c>.
43+
/// Returns <see langword="null"/> if no matching entity is found.
44+
/// </remarks>
3145
protected override async ValueTask<TReadModel?> Process(
3246
EntityIdentifierQuery<TKey, TReadModel> request,
3347
CancellationToken cancellationToken = default)

src/Arbiter.CommandQuery.EntityFramework/Handlers/EntityIdentifiersQueryHandler.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@
99
namespace Arbiter.CommandQuery.EntityFramework.Handlers;
1010

1111
/// <summary>
12-
/// A handler for a request that reads a collection of entities in the specified <see cref="DbContext"/>.
12+
/// A handler that processes an <see cref="EntityIdentifiersQuery{TKey, TReadModel}"/> by querying the
13+
/// specified <typeparamref name="TContext"/> for all <typeparamref name="TEntity"/> instances whose primary
14+
/// key is contained in the request's identifier list, and projecting the results to
15+
/// <typeparamref name="TReadModel"/>.
1316
/// </summary>
14-
/// <inheritdoc/>
17+
/// <typeparam name="TContext">The <see cref="DbContext"/> used to access the data store.</typeparam>
18+
/// <typeparam name="TEntity">The entity type being queried. Must implement <see cref="IHaveIdentifier{TKey}"/> and have a parameterless constructor.</typeparam>
19+
/// <typeparam name="TKey">The type of the entity's primary key.</typeparam>
20+
/// <typeparam name="TReadModel">The output model type returned for each matched entity.</typeparam>
1521
public class EntityIdentifiersQueryHandler<TContext, TEntity, TKey, TReadModel>
1622
: EntityDataContextHandlerBase<TContext, TEntity, TKey, TReadModel, EntityIdentifiersQuery<TKey, TReadModel>, IReadOnlyList<TReadModel>>
1723
where TContext : DbContext
@@ -21,13 +27,24 @@ public class EntityIdentifiersQueryHandler<TContext, TEntity, TKey, TReadModel>
2127
/// <summary>
2228
/// Initializes a new instance of the <see cref="EntityIdentifiersQueryHandler{TContext, TEntity, TKey, TReadModel}"/> class.
2329
/// </summary>
24-
/// <inheritdoc/>
30+
/// <param name="loggerFactory">The logger factory used to create an <see cref="ILogger"/> for this handler.</param>
31+
/// <param name="dataContext">The <typeparamref name="TContext"/> used to access the data store.</param>
32+
/// <param name="mapper">The <see cref="IMapper"/> used to project <typeparamref name="TEntity"/> to <typeparamref name="TReadModel"/>.</param>
33+
/// <param name="pipeline">An optional <see cref="IQueryPipeline"/> applied to queries executed by this handler.</param>
34+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="loggerFactory"/>, <paramref name="dataContext"/>, or <paramref name="mapper"/> is <see langword="null"/>.</exception>
2535
public EntityIdentifiersQueryHandler(ILoggerFactory loggerFactory, TContext dataContext, IMapper mapper, IQueryPipeline? pipeline = null)
2636
: base(loggerFactory, dataContext, mapper, pipeline)
2737
{
2838
}
2939

3040
/// <inheritdoc/>
41+
/// <remarks>
42+
/// Applies any optional query pipeline modifiers, then filters the <see cref="DbSet{TEntity}"/>
43+
/// to entities whose <c>Id</c> is contained in <c>request.Ids</c>. The matched entities are
44+
/// projected to <typeparamref name="TReadModel"/> and returned as an
45+
/// <see cref="IReadOnlyList{T}"/>. Returns an empty list when no identifiers match;
46+
/// returns <see langword="null"/> only if the underlying query returns <see langword="null"/>.
47+
/// </remarks>
3148
protected override async ValueTask<IReadOnlyList<TReadModel>?> Process(
3249
EntityIdentifiersQuery<TKey, TReadModel> request,
3350
CancellationToken cancellationToken = default)

0 commit comments

Comments
 (0)