Skip to content

Common Mistakes

1. Missing [Resource] — No Endpoints Generated

Section titled “1. Missing [Resource] — No Endpoints Generated”

Wrong:

[Entity<Guid>]
[HasComments] // SG warning PRAG2601
public partial class Reservation { ... }

Right:

[Entity<Guid>]
[Resource("reservations")] // Required for endpoint route resolution
[HasComments]
public partial class Reservation { ... }

Why: The SG needs the resource segment to derive endpoint routes (/api/{boundary}/{segment}/{id}/comments). Without [Resource], entity + config + actions are generated but no HTTP endpoints.

Wrong:

[HasComments] // SG error PRAG2600
public partial class Reservation { ... }

Right:

[Entity<Guid>]
[HasComments]
public partial class Reservation { ... }

Why: [HasComments] requires [Entity<TId>] to determine the parent’s Id type for the FK.

Wrong:

[Entity<Guid>]
[HasComments]
public class Reservation { ... } // Not partial!

Right:

[Entity<Guid>]
[HasComments]
public partial class Reservation { ... }

Why: The SG adds a Comments navigation property via a partial class file. Without partial, the compilation fails.

4. Sending JSON Object for Update Instead of Raw String

Section titled “4. Sending JSON Object for Update Instead of Raw String”

Wrong:

PUT /api/booking/reservations/{id}/comments/{commentId}
{ "content": "Updated" }

Right:

PUT /api/booking/reservations/{id}/comments/{commentId}
"Updated content here"

Why: The Update endpoint binds Content as [FromBody] string content (single scalar parameter, no wrapper DTO). Send the string directly.

5. Not Adding Comment Permissions to Auth Headers

Section titled “5. Not Adding Comment Permissions to Auth Headers”

Wrong:

// Test sends POST but gets 403
var response = await client.PostAsync("/api/booking/reservations/{id}/comments", body);

Right:

client.DefaultRequestHeaders.Add("X-User-Permissions",
"booking.reservation.comments.create");

Why: Generated endpoints use the permission constants from {Entity}CommentPermissions. Ensure the test client has the required permissions.

6. Registering ICommentPolicy Without the Right Generic Type

Section titled “6. Registering ICommentPolicy Without the Right Generic Type”

Wrong:

services.AddScoped<ICommentPolicy<string>, MyPolicy>(); // Wrong TEntityId!

Right:

services.AddScoped<ICommentPolicy<Guid>, MyPolicy>(); // Match entity's TId

Why: The SG resolves ICommentPolicy<TEntityId> where TEntityId matches the parent entity’s Id type (usually Guid).

Wrong:

// After DELETE, expecting the row to be gone from the database
var count = await db.Set<ReservationComment>().CountAsync(); // Still there!

Right:

// Comments are soft-deleted (IsDeleted = true)
// The global query filter hides them from normal queries
var count = await db.Set<ReservationComment>()
.IgnoreQueryFilters()
.Where(c => c.IsDeleted)
.CountAsync();

Why: [HasComments] always uses soft-delete with a global query filter. The row remains in the database with IsDeleted = true, DeletedAt, DeletedBy populated.

Wrong:

[HasComments(EditWindowMinutes = 30)]
// 31 minutes later...
await actions.UpdateReservationComment(action); // 403 Forbidden

Right:

// Edit within the configured window, or set EditWindowMinutes = -1 for no limit
[HasComments(EditWindowMinutes = -1)] // Default: no time limit

Why: When EditWindowMinutes > 0, the Update action checks createdAt + window > now. After expiry, edits are rejected with ForbiddenError.