Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Umbrella Dotnet Scaffold Ef Repository

ASecurity

Scaffold a repository interface, implementation, IncludeMap, and DI registration for an existing EF Core entity, following Umbrella GenericDbRepository patterns.

8 stars
0 votes
0 copies
0 views
Added 9/22/2026
toolsgoexpress

Security Analysis

A100/100

Scanned 9/22/2026

Install to Claude Code

$npx -y skills add umbrella-libraries/Umbrella --skill umbrella-dotnet-scaffold-ef-repository --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Umbrella Dotnet Scaffold Ef Repository?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Umbrella Dotnet Scaffold Ef Repository
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/umbrella-libraries-umbrella-dotnet-scaffold-ef-repository/badge)](https://www.skillsdirectory.com/skills/umbrella-libraries-umbrella-dotnet-scaffold-ef-repository)

More formats (shields.io, HTML) on the badges page.

Download with Pro
Files
SKILL.md
---
name: umbrella-dotnet-scaffold-ef-repository
description: 'Scaffold a repository interface, implementation, IncludeMap, and DI registration for an existing EF Core entity, following Umbrella GenericDbRepository patterns.'
---

# Scaffold Repository

## Purpose

Add a repository interface, implementation class, optional IncludeMap static class, and DI registration for an existing EF Core entity, following the exact patterns used in the target repository.

## Discovery (read these before writing anything)

1. Read 2-3 existing repository implementations in `Core\<AppName>.Core.Data\Repositories\` to understand the query method and override conventions.
2. Read the corresponding interfaces in `Core\<AppName>.Core.Data\Repositories\Abstractions\` to see method signature patterns.
3. Read any existing IncludeMap files in `Core\<AppName>.Core.Data\Repositories\IncludeMaps\` -- only create one for the new entity if similar entities have them.
4. Read `Core\<AppName>.Core.Data\IServiceCollectionExtensions.cs` to understand the DI registration structure.
5. Identify the layer-specific exception type used by existing repositories (e.g., `<AppName>CoreDataException`).

---

## Step 1 -- Create the IncludeMap class

Only create this file if the entity has navigation properties (FK relations, collections) that callers may want to eagerly load. Skip this step for leaf entities with no navigations.

**File location:** `Core\<AppName>.Core.Data\Repositories\IncludeMaps\<EntityName>IncludeMaps.cs`

```csharp
#nullable disable
namespace <AppName>.Core.Data.Repositories.IncludeMaps;

public static class <EntityName>IncludeMaps
{
    public static IncludeMap<<EntityName>> All { get; } = new(x => x.Navigation1, x => x.Navigation2);
    public static IncludeMap<<EntityName>> Navigation1Only { get; } = new(x => x.Navigation1);
}
```

**Rules:**
- `#nullable disable` is required at the top of every IncludeMap file
- Name each property after the loading scenario or the navigation it loads (e.g., `SENDDetails`, `All`, `WithAuthor`)
- One property per logical loading scenario; if only one navigation exists, one property named after it is sufficient
- `IncludeMap<T>` takes `params Expression<Func<T, object?>>[]` -- pass navigations as lambda expressions

---

## Step 2 -- Create the interface

**File location:** `Core\<AppName>.Core.Data\Repositories\Abstractions\I<EntityName>Repository.cs`

```csharp
namespace <AppName>.Core.Data.Repositories.Abstractions;

public interface I<EntityName>Repository : IGenericDbRepository<<EntityName>>
{
    Task<<EntityName>?> FindByXxxAsync(string value, bool trackChanges = false, IncludeMap<<EntityName>>? map = null, CancellationToken cancellationToken = default);
}
```

**Rules:**
- No `using` directives -- all required types flow via global usings defined in the `.csproj`
- Inherits `IGenericDbRepository<<EntityName>>` -- this provides standard CRUD for free
- Add only query methods the entity genuinely needs beyond standard CRUD
- Standard tail parameters on every query method: `bool trackChanges = false, IncludeMap<<EntityName>>? map = null, CancellationToken cancellationToken = default`
- Omit `trackChanges` and `map` on methods that return non-entity projections (e.g., `IReadOnlyCollection<string>`, `bool`, `int`)
- Return types: `Task<<EntityName>?>` for single-or-null, `Task<IReadOnlyCollection<<EntityName>>>` for lists, `Task<bool>` for existence checks

---

## Step 3 -- Create the implementation

**File location:** `Core\<AppName>.Core.Data\Repositories\<EntityName>Repository.cs`

**Minimal pattern (no extra dependencies):**

```csharp
using Umbrella.Utilities.Dating.Abstractions;

namespace <AppName>.Core.Data.Repositories;

internal sealed class <EntityName>Repository : GenericDbRepository<<EntityName>, <AppName>DbContext>, I<EntityName>Repository
{
    public <EntityName>Repository(
        Lazy<<AppName>DbContext> dbContext,
        ILogger<<EntityName>Repository> logger,
        IDataLookupNormalizer lookupNormalizer,
        IUmbrellaDbContextHelper dbContextHelper,
        IEntityValidator entityValidator,
        IDateTimeProvider dateTimeProvider)
        : base(dbContext, logger, lookupNormalizer, dbContextHelper, entityValidator, dateTimeProvider)
    {
    }
}
```

**With extra service dependencies:**

```csharp
using Umbrella.Utilities.Dating.Abstractions;
using <Namespace.Of.ExtraService>;

namespace <AppName>.Core.Data.Repositories;

internal sealed class <EntityName>Repository : GenericDbRepository<<EntityName>, <AppName>DbContext>, I<EntityName>Repository
{
    private readonly IExtraService _extraService;

    public <EntityName>Repository(
        Lazy<<AppName>DbContext> dbContext,
        ILogger<<EntityName>Repository> logger,
        IDataLookupNormalizer lookupNormalizer,
        IUmbrellaDbContextHelper dbContextHelper,
        IEntityValidator entityValidator,
        IDateTimeProvider dateTimeProvider,
        IExtraService extraService)
        : base(dbContext, logger, lookupNormalizer, dbContextHelper, entityValidator, dateTimeProvider)
    {
        _extraService = extraService;
    }
}
```

**Rules:**
- `using Umbrella.Utilities.Dating.Abstractions;` is always required -- it contains `IDateTimeProvider`
- Add extra `using` directives only for services injected beyond the 6 base dependencies
- Always `internal sealed class` -- never `public`, never non-sealed
- Second generic parameter is the concrete DbContext class in this project (e.g., `<AppName>DbContext`)
- Base 6 dependencies in this exact order: `Lazy<TDbContext>`, `ILogger<TRepo>`, `IDataLookupNormalizer`, `IUmbrellaDbContextHelper`, `IEntityValidator`, `IDateTimeProvider`
- Extra dependencies go AFTER the 6 base params; pass only the 6 base params to `: base(...)`
- Store extra deps as `private readonly` fields

---

## Step 4 -- Write query methods

Every query method follows this pattern:

```csharp
public async Task<<EntityName>?> FindByXxxAsync(
    string value,
    bool trackChanges = false,
    IncludeMap<<EntityName>>? map = null,
    CancellationToken cancellationToken = default)
{
    cancellationToken.ThrowIfCancellationRequested();
    Guard.IsNotNullOrWhiteSpace(value);

    try
    {
        return await Items
            .TrackChanges(trackChanges)
            .IncludeMap(map)
            .SingleOrDefaultAsync(x => x.Field == value, cancellationToken);
    }
    catch (Exception exc) when (Logger.WriteError(exc, new { value, trackChanges, map }))
    {
        throw new <AppName>CoreDataException("There has been a problem getting the <entityName> with the specified <description>.", exc);
    }
}
```

**Rules:**
- First line always: `cancellationToken.ThrowIfCancellationRequested();`
- Validate string inputs immediately after with `Guard.IsNotNullOrWhiteSpace(param)`; use `Guard.IsNotNull(param)` for non-string reference types; no Guard call for value types
- Wrap every async query in `try/catch (Exception exc) when (Logger.WriteError(exc, new { ... }))` -- the catch block never executes, only the filter
- The anonymous object in `Logger.WriteError` should include the method's input parameters (omit `cancellationToken`)
- Re-throw as the layer-specific exception: `throw new <AppName>CoreDataException("...", exc);`
- Use `Items` as the query starting point -- this is a pre-filtered IQueryable from the base class
- Chain `.TrackChanges(trackChanges).IncludeMap(map)` on entity-returning methods; omit both on projection queries (e.g., `.Select(x => x.Name).ToListAsync(...)`)
- Use `Context.Value.Set<TOtherEntity>()` only when you genuinely need to query a different entity type

**Override methods (add only when the entity needs them):**

| Override | When to use |
|---|---|
| `SanitizeEntityAsync` | Normalize or compute derived fields before save (e.g., URL slugs, normalized lookup keys) |
| `ValidateEntityAsync` | Enforce uniqueness constraints or business rules beyond attribute validation |
| `BeforeContextSavingAsync` | Side effects just before EF SaveChanges |
| `BeforeContextDeletingAsync` | Side effects just before EF deletes the entity |
| `AfterContextSavedChangesAsync` | Post-save actions (e.g., queue background work, clear caches) |

Always call the base method first. For `ValidateEntityAsync`, add to the returned collection:

```csharp
protected override async Task SanitizeEntityAsync(<EntityName> entity, RepoOptions options, IEnumerable<RepoOptions>? childOptions, CancellationToken cancellationToken)
{
    await base.SanitizeEntityAsync(entity, options, childOptions, cancellationToken);
    entity.NameNormalized = _someService.Normalize(entity.Name);
}

protected override async Task<ICollection<ValidationResult>> ValidateEntityAsync(<EntityName> entity, RepoOptions options, IEnumerable<RepoOptions>? childOptions, CancellationToken cancellationToken)
{
    cancellationToken.ThrowIfCancellationRequested();
    var lstValidationResult = await base.ValidateEntityAsync(entity, options, childOptions, cancellationToken);

    if (await ExistsByNameAsync(entity.Name, entity.Id, cancellationToken))
        lstValidationResult.Add(new ValidationResult("The name must be unique.", new[] { nameof(entity.Name) }));

    return lstValidationResult;
}
```

Add `using System.ComponentModel.DataAnnotations;` to the implementation file if you override `ValidateEntityAsync`.

---

## Step 5 -- Register in DI

**File:** `Core\<AppName>.Core.Data\IServiceCollectionExtensions.cs`

Add one line in the `// Repositories` section of the `AddXxx(this IServiceCollection services)` method, in alphabetical order:

```csharp
_ = services.AddScoped<I<EntityName>Repository, <EntityName>Repository>();
```

---

## Analyzer compatibility

Before finishing, read `.ai-shared\bundles\umbrella\analyzer-compatibility.md` and build the affected projects with their installed analyzers enabled. Treat diagnostics introduced by the generated or changed code as defects in this workflow.

## Verification

1. If an IncludeMap file was created, confirm it has `#nullable disable` and only references navigations that exist on the entity.
2. Confirm the interface inherits `IGenericDbRepository<<EntityName>>` with no extra `using` directives.
3. Confirm the implementation is `internal sealed`, the constructor passes exactly the 6 base params to `: base(...)`, and `using Umbrella.Utilities.Dating.Abstractions;` is present.
4. Confirm every query method starts with `cancellationToken.ThrowIfCancellationRequested()`, validates string params with Guard, and uses the try/catch pattern.
5. Confirm `_ = services.AddScoped<I<EntityName>Repository, <EntityName>Repository>();` is present in `IServiceCollectionExtensions.cs`.

Attribution

umbrella-librariesumbrella-libraries
View sourceMore from umbrella-libraries →
SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Related Skills

ucoz-landing-skill

Playbook for creating and editing uCoz landing pages via MCP tools (`templates_tool`, `ftp_tool`, `modules_tool`). Use for tasks such as: "build a landing page", "update the homepage as a landing page", "create a promo page on the homepage", "add a lead form / menu / SEO to the homepage". Homepage: `page_list`, `page_get`; first publish — `page_update` with full `page_tmpl`; HTML edits after generation — `patch_template` (module_id=2, template_id=1), not `update_template`. Activate the mail f...

107 votes

Paperclip

Interact with the Paperclip control plane API for task coordination and governance. Use when checking assignments, updating issue status, posting comments, delegating work, managing routines, or calling Paperclip API endpoints.

805541 votes

Instantly Rdsthomas Mission Control

Instantly.ai cold email outreach API - manage campaigns, leads, accounts, and analytics. Use for cold email automation, lead management, campaign creation/monitoring, and email account warmup.

761 votes

Daw Music

Digital Audio Workstation usage, music composition, interactive music systems, and game audio implementation for immersive soundscapes.

761 votes

Caveman Compress

Compress natural language memory files (CLAUDE.md, todos, preferences) into caveman format to save input tokens. Preserves all technical substance, code, URLs, and structure. Compressed version overwrites the original file. Human-readable backup saved as FILE.original.md. Trigger: /caveman-compress FILEPATH or "compress memory file"

1066600 votes
View all in tools →