Getting Started with ASP.NET Core
This guide shows how to add CrestApps.Core services to any ASP.NET Core host model. The same framework packages and registration chain work across MVC, Razor Pages, Blazor, Minimal APIs, and .NET MAUI hybrid apps.
1. Choose packages
Start with the smallest set that matches your scenario.
<ItemGroup>
<PackageReference Include="CrestApps.Core" />
<PackageReference Include="CrestApps.Core.AI" />
<PackageReference Include="CrestApps.Core.AI.Chat" />
<!-- Add when you need uploaded-document ingestion, document RAG, or document endpoints -->
<PackageReference Include="CrestApps.Core.AI.Documents" />
<PackageReference Include="CrestApps.Core.AI.Documents.OpenXml" />
<PackageReference Include="CrestApps.Core.AI.Documents.Pdf" />
<!-- Pick at least one provider -->
<PackageReference Include="CrestApps.Core.AI.OpenAI" />
<!-- or CrestApps.Core.AI.OpenAI.Azure -->
<!-- or CrestApps.Core.AI.Ollama -->
<!-- or CrestApps.Core.AI.AzureAIInference -->
<!-- Optional but common -->
<PackageReference Include="CrestApps.Core.AI.Markdown" />
<PackageReference Include="CrestApps.Core.Templates" />
<PackageReference Include="CrestApps.Core.SignalR" />
<PackageReference Include="CrestApps.Core.Data.EntityCore" />
<!-- or CrestApps.Core.Data.YesSql -->
</ItemGroup>
2. Register services
In Program.cs, compose the framework through the AddCrestAppsCore(...) builder:
builder.Services.AddCrestAppsCore(crestApps => crestApps
.AddAISuite(ai => ai
.AddOpenAI()
.AddChatInteractions()
)
);
AddAISuite(...) adds the shared CrestApps core services, the AI runtime, and orchestration together. If you prefer the lower-level registrations, the same features are still available as raw IServiceCollection extensions such as AddCoreAIServices() and AddCoreAIOrchestration().
Then add only the host-specific pieces you need:
builder.Services.AddCoreSignalR();
3. Configure connections and deployments
At minimum, provide a connection and a deployment through configuration or your own catalog/store implementation.
{
"CrestApps": {
"AI": {
"Connections": [
{
"Name": "primary-openai",
"ClientName": "OpenAI",
"ApiKey": "YOUR_API_KEY"
}
],
"Deployments": [
{
"Name": "gpt-4.1",
"ClientName": "OpenAI",
"Type": "Chat",
"ModelName": "gpt-4.1"
}
]
}
}
}
The default configuration sections are:
CrestApps:AI:Connectionsfor provider connections and credentialsCrestApps:AI:Deploymentsfor standalone deployments and deployment metadata
Keep connection credentials under Connections and define model/deployment choices under Deployments.
Fastest path to a first prompt
For the smallest useful setup:
- Register
AddAISuite(...) - Add one provider like
AddOpenAI() - Add
AddChatInteractions() - Configure one connection in
CrestApps:AI:Connections - Configure one chat deployment in
CrestApps:AI:Deployments - Create an AI profile that points at that deployment
- Use Chat Interactions as the first playground-style UI
That path gets you to a working chat experience with the fewest moving parts.
4. Pick the application model
The service registrations stay the same; only the UI or endpoint layer changes.
MVC
Use MVC when you want server-rendered admin pages, controllers, and SignalR chat hubs. The reference implementation is src\Startup\CrestApps.Core.Mvc.Web.
Use these files as the primary sample:
src\Startup\CrestApps.Core.Mvc.Web\Program.csfor service registration and feature compositionsrc\Startup\CrestApps.Core.Mvc.Web\appsettings.jsonfor connection and deployment examples
Razor Pages
Use the same DI setup and inject framework services into page models:
public sealed class ChatModel : PageModel
{
private readonly IOrchestrator _orchestrator;
public ChatModel(IOrchestrator orchestrator)
{
_orchestrator = orchestrator;
}
}
Blazor Server / Blazor Web App
Inject IAICompletionService, IOrchestrator, or IAIClientFactory into components and stream partial results to the UI. AddCoreSignalR() remains useful when you want hub-based browser communication outside normal Blazor circuits.
Minimal APIs
Expose thin endpoints over the framework services:
app.MapPost("/chat", async (IOrchestrator orchestrator, ChatRequest request) =>
{
// Build context, execute orchestration, and return the result.
});
.NET MAUI Hybrid / Blazor Hybrid
Register the same services in the app host and use them from native pages or hybrid components. This lets you share provider configuration, orchestration, tools, and document logic between desktop/mobile shells and your web hosts.
5. Persist state when needed
If your app needs durable profiles, sessions, templates, or connection records, plug in a store implementation. The repository includes two first-party options:
CrestApps.Core.Data.EntityCorefor Entity Framework Core-backed catalogs and storesCrestApps.Core.Data.YesSqlfor YesSql-backed catalogs and stores
Each feature has its own per-feature store registration extension so you can see exactly which stores each feature needs:
Entity Framework Core — configure the data store, then register stores per feature via the builder:
builder.Services.AddCrestAppsCore(crestApps => crestApps
.AddAISuite(ai => ai
.AddEntityCoreStores()
.AddOpenAI()
.AddChatInteractions(ci => ci
.AddEntityCoreStores()
)
.AddDocumentProcessing(dp => dp
.AddEntityCoreStores()
.AddOpenXml()
.AddPdf()
)
.AddAIMemory(memory => memory
.AddEntityCoreStores()
)
.AddA2AClient(a2a => a2a
.AddEntityCoreStores()
)
.AddMcpClient(mcp => mcp
.AddEntityCoreStores()
)
.AddMcpServer(mcpServer => mcpServer
.AddEntityCoreStores()
)
)
.AddIndexingServices(indexing => indexing
.AddEntityCoreStores()
)
.AddEntityCoreSqliteDataStore(
$"Data Source={Path.Combine(builder.Environment.ContentRootPath, "App_Data", "crestapps.db")}")
);
YesSql — configure the data store, then register stores per feature via the builder:
builder.Services.AddCrestAppsCore(crestApps => crestApps
.AddAISuite(ai => ai
.AddYesSqlStores() // AIProfile, AIProfileTemplate, connections, deployments, chat sessions
.AddOpenAI()
.AddChatInteractions(ci => ci
.AddYesSqlStores() // ChatInteraction, IChatInteractionPromptStore
)
.AddDocumentProcessing(dp => dp
.AddYesSqlStores() // IAIDocumentStore, IAIDocumentChunkStore, IAIDataSourceStore
.AddOpenXml()
.AddPdf()
)
.AddAIMemory(memory => memory
.AddYesSqlStores() // IAIMemoryStore
)
.AddA2AClient(a2a => a2a
.AddYesSqlStores() // A2AConnection
)
.AddMcpClient(mcp => mcp
.AddYesSqlStores() // McpConnection
)
.AddMcpServer(mcpServer => mcpServer
.AddYesSqlStores() // McpPrompt, McpResource
)
)
.AddIndexingServices(indexing => indexing
.AddYesSqlStores() // ISearchIndexProfileStore
)
.AddYesSqlDataStore(configuration => configuration
.UseSqLite("Data Source=app.db;Cache=Shared")
)
);
Both Entity Framework Core and the built-in YesSql stores follow the same IStoreCommitter pattern. Register the MVC action filter, the Minimal API endpoint filter, and the existing SignalR store-committer filter when your store implementation uses a unit-of-work/session model. If your custom implementation persists immediately and does not stage tracked changes, you do not need IStoreCommitter.
For MVC actions:
builder.Services
.AddControllersWithViews()
.AddCrestAppsStoreCommitterFilter();
For Minimal APIs:
app.MapGroup("/api")
.AddEndpointFilter<StoreCommitterEndpointFilter>();
If you already use another ORM or storage model, implement the same catalog/store abstractions against your preferred backend. See Data Storage for the full per-feature store reference.
6. Keep site-settings-backed options live
If your host maps admin-managed site settings into options, use IOptionsMonitor<TOptions> for consumers and notify the monitor through an IOptionsChangeTokenSource<TOptions> when the site settings store is saved.
The shared sample hosts already follow this pattern. After they call SiteSettingsStore.SaveChangesAsync(), the store rotates its change token and IOptionsMonitor<> rebuilds the option values on the next CurrentValue access.
For a custom host, the minimal pattern is:
public sealed class SiteSettingsStore
{
private CancellationTokenSource _reloadTokenSource = new();
public IChangeToken GetChangeToken()
=> new CancellationChangeToken(_reloadTokenSource.Token);
public async Task SaveChangesAsync()
{
// Persist the updated site settings first.
await PersistAsync();
var previous = Interlocked.Exchange(ref _reloadTokenSource, new CancellationTokenSource());
previous.Cancel();
previous.Dispose();
}
}
internal sealed class SiteSettingsOptionsChangeTokenSource<TOptions> : IOptionsChangeTokenSource<TOptions>
{
private readonly SiteSettingsStore _siteSettings;
public SiteSettingsOptionsChangeTokenSource(SiteSettingsStore siteSettings)
{
_siteSettings = siteSettings;
}
public string Name => Options.DefaultName;
public IChangeToken GetChangeToken() => _siteSettings.GetChangeToken();
}
Register the change-token source once, keep your IConfigureOptions<TOptions> mapping, and then consume IOptionsMonitor<TOptions> anywhere you need current values:
services.AddSingleton<SiteSettingsStore>();
services.TryAddEnumerable(
ServiceDescriptor.Singleton(typeof(IOptionsChangeTokenSource<>), typeof(SiteSettingsOptionsChangeTokenSource<>)));
services.AddSingleton<IConfigureOptions<GeneralAIOptions>, SiteSettingsConfigureGeneralAIOptions>();
That keeps settings refresh host-agnostic and avoids custom accessor interfaces.
7. Add features one layer at a time
The intended progression is:
AddCrestAppsCore(builder => ...)as the single CrestApps entrypointAddAISuite(...)for the shared foundation, AI runtime, and orchestration primitives- Provider registration inside the AI suite such as
AddOpenAI() - Higher-level capabilities like chat, document processing, MCP, A2A, SignalR, templates, and custom tools
That layering keeps small apps lightweight while letting larger apps grow into a full AI platform without changing architectural direction.
8. Use the MVC sample as the reference host
src\Startup\CrestApps.Core.Mvc.Web\Program.cs is the canonical example for:
- configuration layering
- service registration order
- provider registration blocks
- storage registration
- MCP and A2A setup
- SignalR and chat integration
- feature-by-feature opt-in extensions
Use that sample when you want to add one capability at a time and compare a minimal setup with a full production-style composition.