A lightweight library for applying OData query options ($filter, $orderby, $skip, $top, $select, $apply) to IQueryable<T> and IEnumerable<T> collections in ASP.NET Core applications.
- Apply OData query options from HTTP request to any collection
- Fluent extension methods for IQueryable and IEnumerable
- Configurable options (page size, ignored query options, null propagation)
- Case-insensitive
$filtersupport (configurable collation) IODataServiceinterface for easy mocking/testing- Generic
RegisterEntitiesFromAssemblies<TMarker>()- use your own marker interface - Full XML documentation for IntelliSense support
dotnet add package Kebechet.Api.OData// Basic setup with default options
services.AddOData(builder =>
{
builder.RegisterEntitiesFromAssemblies<IEntity>(typeof(MyEntity).Assembly);
});
// With manual type registration
services.AddOData(builder =>
{
builder.RegisterTypes(
typeof(ProductResponse),
typeof(OrderResponse),
typeof(CustomerResponse)
);
});
// With custom options
services.AddOData(
options =>
{
options.PageSize = 100;
options.IgnoreExpand = false;
options.IgnoreCount = false;
},
builder =>
{
builder.RegisterEntitiesFromAssemblies<IEntity>(typeof(MyEntity).Assembly);
});public class ProductService
{
private readonly AppDbContext _dbContext;
private readonly IODataService _oDataService;
public ProductService(AppDbContext dbContext, IODataService oDataService)
{
_dbContext = dbContext;
_oDataService = oDataService;
}
public async Task<List<Product>> GetProductsAsync()
{
return await _dbContext.Products
.Where(p => !p.IsDeleted)
.ApplyODataQuery(_oDataService)
.ToListAsync();
}
}public async Task<List<Product>> GetProductsAsync()
{
return await _dbContext.Products
.ApplyODataFilter(_oDataService) // only $filter from request
.OrderByDescending(p => p.CreatedAt) // custom ordering
.Take(50) // custom limit
.ToListAsync();
}query.ApplyODataQuery(oDataService); // all OData options
query.ApplyODataFilter(oDataService); // $filter
query.ApplyODataOrderBy(oDataService); // $orderby
query.ApplyODataPagination(oDataService); // $skip and $top
query.ApplyODataSelect(oDataService); // $select
query.ApplyODataApply(oDataService); // $apply
// Conditional application
query.ApplyODataFilter(oDataService, isEnabled: shouldFilter);| Option | Default | Description |
|---|---|---|
PageSize |
null |
Maximum page size for results. Null means no limit. |
IgnoreExpand |
true |
When true, $expand query option is ignored. |
IgnoreCount |
true |
When true, $count query option is ignored. |
HandleNullPropagation |
False |
How null propagation is handled during query composition. |
EnableCaseInsensitiveFilter |
false |
When true, string comparisons in $filter are case-insensitive. |
CaseInsensitiveCollation |
"Latin1_General_CI_AS" |
Collation for case-insensitive comparisons. Default is SQL Server. For SQLite, use "NOCASE". |
services.AddOData(
options =>
{
options.EnableCaseInsensitiveFilter = true;
// Default collation is "Latin1_General_CI_AS" (SQL Server)
// For SQLite use: options.CaseInsensitiveCollation = "NOCASE";
},
builder =>
{
builder.RegisterEntitiesFromAssemblies<IEntity>(typeof(MyEntity).Assembly);
});Query ?$filter=Name eq 'john' will match "John", "JOHN", "john", etc.
$apply$filter$orderby$skip$top$select
This repository is licensed with the MIT license.
This work was financially supported by the European Union under project CZ.01.01.01/01/22_00-2/0000791.

