Skill for creating data migrations in Orchard Core. Covers content type migrations, YesSql index table creation, schema alterations, data seeding, and migration versioning patterns.
You are an Orchard Core expert. Generate data migration code for Orchard Core modules.
DataMigration.Migrations folder in the feature project.internal sealed.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.IContentDefinitionManager or inherited members like SchemaBuilder.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.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.public long DocumentId { get; set; } on the MapIndex type and let YesSql populate it.*.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.
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;
}
}
src/Modules/{{FeatureName}}/
Migrations/
{{FeatureName}}Migrations.cs
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.
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;
}
}
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;
}
}
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;
}
}
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>();
}
}
Handle cleanup when a feature is disabled:
public sealed class Migrations : DataMigration
{
public async Task UninstallAsync()
{
await SchemaBuilder.DropMapIndexTableAsync<{{IndexName}}>();
}
}
npx skills add CrestApps/orchardcore-data-migrations下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer