# Orchardcore Data Migrations

> Skill for creating data migrations in Orchard Core. Covers content type migrations, YesSql index table creation, schema alterations, data seeding, and migration versioning patterns. Use this skill when requests mention Orchard Core Data Migrations, Create Data Migrations, Basic Migration with Content Type, Folder and Class Conventions, Migration with Custom Content Part and Fields, Migration with YesSql Index Table, or closely related Orchard Core implementation, setup, extension, or troubleshooting work. Strong matches include work with OrchardCore.ContentManagement.Metadata, OrchardCore.Data.Migration, CrestApps.Sports.Teams.Migrations, DataMigration, IDataMigration, IContentDefinitionManager, SchemaBuilder, WithPart. It also helps with data migrations examples, Migration with Custom Content Part and Fields, Migration with YesSql Index Table, Incremental Migration (UpdateFrom), plus the code patterns, admin flows, recipe steps, and referenced examples captured in this skill.

- Skill: `crestapps/orchardcore-data-migrations` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add crestapps/orchardcore-data-migrations`
- Raw SKILL.md: https://api.skillmd.com/api/skills/crestapps/orchardcore-data-migrations/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: Apache-2.0
- Author: CrestApps (https://skillmd.com/u/crestapps)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/crestapps/orchardcore-data-migrations

---


# Orchard Core Data Migrations - Prompt Templates

## Create Data Migrations

You are an Orchard Core expert. Generate data migration code for Orchard Core modules.

### Guidelines

- Data migrations inherit from `DataMigration`.
- Place migrations in a `Migrations` folder in the feature project.
- Name migration classes clearly and keep them `internal sealed`.
- Migrations define content types, parts, fields, and database indexes.
- Use `CreateAsync()` for initial migration, `UpdateFrom1Async()`, `UpdateFrom2Async()` for incremental updates.
- `Create`, `CreateAsync`, `UpdateFromX`, `UpdateFromXAsync`, `Uninstall`, and `UninstallAsync` can be marked `static` when they do not use injected services, inherited instance members, or other instance state.
- Keep migration methods instance methods when they rely on constructor-injected services like `IContentDefinitionManager` or inherited members like `SchemaBuilder`.
- Register migrations in `Startup.cs` using `services.AddScoped<IDataMigration, Migrations>()`.
- `IContentDefinitionManager` is used to define content types and parts.
- `SchemaBuilder` is used to create and alter YesSql index tables.
- Use stable column names in `CreateMapIndexTableAsync()` and `AlterIndexTableAsync()`. A string literal is clearest for a persisted schema; use `nameof(...)` only when the member name is intentionally the persisted column name.
- `MapIndex` tables already get a `DocumentId` column from YesSql. Never declare or alter that column manually in the migration.
- If an index needs access to the YesSql document id, expose `public long DocumentId { get; set; }` on the `MapIndex` type and let YesSql populate it.
- Prefer keeping reusable content-part models in the corresponding `*.Core` project when they are used outside the feature wiring layer.

For Orchard Core 3.0 migrations, use the current async migration and clock
APIs. `IDataMigrationManager.UpdateAsync` accepts the feature names to update,
and `ILocalClock.GetLocalNowAsync` replaces the removed local-clock async
member. Do not add `IAsyncOptions` or `IAsyncConfigureOptions<TOptions>`;
configure options with the synchronous options APIs.

### Basic Migration with Content Type

```csharp
using OrchardCore.ContentManagement.Metadata;
using OrchardCore.ContentManagement.Metadata.Settings;
using OrchardCore.Data.Migration;

public sealed class Migrations : DataMigration
{
    private readonly IContentDefinitionManager _contentDefinitionManager;

    public Migrations(IContentDefinitionManager contentDefinitionManager)
    {
        _contentDefinitionManager = contentDefinitionManager;
    }

    public async Task<int> CreateAsync()
    {
        await _contentDefinitionManager.AlterTypeDefinitionAsync("{{ContentType}}", type => type
            .DisplayedAs("{{DisplayName}}")
            .Creatable()
            .Listable()
            .Draftable()
            .Versionable()
            .WithPart("TitlePart", part => part.WithPosition("0"))
            .WithPart("AutoroutePart", part => part
                .WithPosition("1")
                .WithSettings(new AutoroutePartSettings
                {
                    AllowCustomPath = true,
                    Pattern = "{{ ContentItem | display_text | slugify }}"
                })
            )
            .WithPart("HtmlBodyPart", part => part
                .WithPosition("2")
                .WithEditor("Wysiwyg")
            )
            .WithPart("{{ContentType}}Part", part => part.WithPosition("3"))
        );

        return 1;
    }
}
```

### Folder and Class Conventions

```text
src/Modules/{{FeatureName}}/
  Migrations/
    {{FeatureName}}Migrations.cs
```

```csharp
using OrchardCore.Data.Migration;

namespace CrestApps.Sports.Teams.Migrations;

internal sealed class TeamMigrations : DataMigration
{
    public static Task<int> CreateAsync()
    {
        // migration steps
        return Task.FromResult(1);
    }
}
```

Use `static` for migration methods only when the body does not touch injected services, `SchemaBuilder`, or any other instance members.

### Migration with Custom Content Part and Fields

```csharp
public sealed class Migrations : DataMigration
{
    private readonly IContentDefinitionManager _contentDefinitionManager;

    public Migrations(IContentDefinitionManager contentDefinitionManager)
    {
        _contentDefinitionManager = contentDefinitionManager;
    }

    public async Task<int> CreateAsync()
    {
        // Define the custom part with fields
        await _contentDefinitionManager.AlterPartDefinitionAsync("{{PartName}}", part => part
            .WithField("{{FieldName}}", field => field
                .OfType("TextField")
                .WithDisplayName("{{FieldDisplayName}}")
                .WithPosition("0")
                .WithSettings(new TextFieldSettings
                {
                    Required = true,
                    Hint = "{{FieldHint}}"
                })
            )
            .WithField("Description", field => field
                .OfType("TextField")
                .WithDisplayName("Description")
                .WithPosition("1")
                .WithEditor("TextArea")
            )
            .WithField("Image", field => field
                .OfType("MediaField")
                .WithDisplayName("Image")
                .WithPosition("2")
            )
        );

        // Define the content type with the custom part
        await _contentDefinitionManager.AlterTypeDefinitionAsync("{{ContentType}}", type => type
            .DisplayedAs("{{DisplayName}}")
            .Creatable()
            .Listable()
            .WithPart("TitlePart", part => part.WithPosition("0"))
            .WithPart("{{PartName}}", part => part.WithPosition("1"))
        );

        return 1;
    }
}
```

### Migration with YesSql Index Table

```csharp
using OrchardCore.Data.Migration;
using YesSql.Sql;

public sealed class Migrations : DataMigration
{
    public async Task<int> CreateAsync()
    {
        await SchemaBuilder.CreateMapIndexTableAsync<{{IndexName}}>(table => table
            .Column<string>("ContentItemId", col => col.WithLength(26))
            .Column<string>("{{PropertyName}}", col => col.WithLength(256))
            .Column<bool>("Published")
            .Column<DateTime>("CreatedUtc")
        );

        await SchemaBuilder.AlterIndexTableAsync<{{IndexName}}>(table => table
            .CreateIndex(
                "IDX_{{IndexName}}_{{PropertyName}}",
                "{{PropertyName}}",
                "Published"
            )
        );

        return 1;
    }
}
```

### Incremental Migration (UpdateFrom)

```csharp
public sealed class Migrations : DataMigration
{
    private readonly IContentDefinitionManager _contentDefinitionManager;

    public Migrations(IContentDefinitionManager contentDefinitionManager)
    {
        _contentDefinitionManager = contentDefinitionManager;
    }

    public async Task<int> CreateAsync()
    {
        // Initial migration
        await _contentDefinitionManager.AlterTypeDefinitionAsync("{{ContentType}}", type => type
            .DisplayedAs("{{DisplayName}}")
            .Creatable()
            .Listable()
            .WithPart("TitlePart")
        );

        return 1;
    }

    public async Task<int> UpdateFrom1Async()
    {
        // Add a new field in version 2
        await _contentDefinitionManager.AlterPartDefinitionAsync("{{ContentType}}", part => part
            .WithField("Category", field => field
                .OfType("TextField")
                .WithDisplayName("Category")
                .WithPosition("2")
            )
        );

        return 2;
    }

    public async Task<int> UpdateFrom2Async()
    {
        // Add an index table in version 3
        await SchemaBuilder.CreateMapIndexTableAsync<{{ContentType}}Index>(table => table
            .Column<string>("ContentItemId", col => col.WithLength(26))
            .Column<string>("Category", col => col.WithLength(256))
            .Column<bool>("Published")
        );

        return 3;
    }
}
```

### Registering Migrations and Indexes

```csharp
using OrchardCore.Data.Migration;
using YesSql.Indexes;

public enum {{IndexName}}Status
{
    One,
    Two,
}

public sealed class {{IndexName}} : MapIndex
{
    public long DocumentId { get; set; }

    public string ContentItemId { get; set; }

    public {{IndexName}}Status Status { get; set; }
}

await SchemaBuilder.CreateMapIndexTableAsync<{{IndexName}}>(table => table
    .Column<string>("ContentItemId", col => col.WithLength(26))
    .Column<{{IndexName}}Status>("Status")
);

public sealed class Startup : StartupBase
{
    public override void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<IDataMigration, Migrations>();
        services.AddIndexProvider<{{IndexName}}Provider>();
    }
}
```

### Uninstall Migration

Handle cleanup when a feature is disabled:

```csharp
public sealed class Migrations : DataMigration
{
    public async Task UninstallAsync()
    {
        await SchemaBuilder.DropMapIndexTableAsync<{{IndexName}}>();
    }
}
```

