Controller Based Queries
You can represent queries as regular ASP.NET Core Controller actions with HTTP GET methods.
public record DebitAccount(AccountId Id, AccountName Name, CustomerId Owner, decimal Balance);
[Route("api/accounts")]
public class Accounts : Controller
{
readonly IMongoCollection<DebitAccount> _collection;
public Accounts(IMongoCollection<DebitAccount> collection) => _collection = collection;
[HttpGet]
public IEnumerable<DebitAccount> AllAccounts() => _collection.Find(_ => true).ToList();
}
Note: This particular model represents its values as concepts - a value type encapsulation that makes us not use primitives - thus creating clearer APIs and models. If you're using the Cratis Arc proxy generator, the method name will become the query name for the generated TypeScript file and class.
Key Features
Controller-based queries provide several powerful features:
- Standard ASP.NET Core routing and HTTP verb support
- Flexible return types including collections, single objects, and custom response wrappers
- Dependency injection for services and repositories
- Query arguments via route parameters, query strings, and request bodies
- Async support for asynchronous operations
- Observable queries for real-time data streaming
- Custom route templates for RESTful API design
When to Use Controller-Based Queries
Controller-based queries are ideal when you:
- Want explicit control over HTTP routing and URL structure
- Need to leverage existing ASP.NET Core features like filters, middleware, or custom attributes
- Are building RESTful APIs with standard HTTP conventions
- Want to separate query logic from your read models
- Need complex routing scenarios with multiple parameters
Bypassing Query Result Wrappers
By default, controller-based queries return results wrapped in a QueryResult structure. If you need to return the raw result from your controller action without this wrapper, you can use the [AspNetResult] attribute. For more details, see Without wrappers.
Related Topics
- Route Templates - Learn about URL routing and parameter binding
- Query Arguments - How to handle different types of query parameters
- Return Types - Understanding different response formats
- Observable Queries - Real-time data streaming with WebSockets
- Dependency Injection - Working with services and repositories
Basic Example with Async Support
For asynchronous operations, you can return Task<T>:
[HttpGet]
public async Task<IEnumerable<DebitAccount>> AllAccountsAsync()
{
var result = await _collection.FindAsync(_ => true);
return result.ToList();
}
Note: The proxy generator automatically creates TypeScript types for your controller methods, making them strongly typed on the frontend as well.