Radzen documentation and examples for versions lower than Net Core 6 and Odata 8.
The first problem is in the response I get from server, does not match in return type.
The second filters
Here is the service configuration:
public async Task<ODataServiceResult<PersonDto>> GetPeopleAsync(string filter = default(string), int? top = default(int?), int? skip = default(int?), string orderby = default(string), string expand = default(string), string select = default(string), bool? count = default(bool?))
{
var uri = new Uri(http.BaseAddress, $"api/Persons");
uri = uri.GetODataUri(filter: filter, top: top, skip: skip, orderby: orderby, expand: expand, select: select, count: count);
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
var response = await http.SendAsync(httpRequestMessage);
return await response.ReadAsync<ODataServiceResult<PersonDto>>();
}
Here is the controller configuration:
[EnableQuery(MaxExpansionDepth = 10, MaxNodeCount = 1000)]
[HttpGet]
public IEnumerable<PersonDto> Get()
{
var people = context.Persons.ConvertToDto().AsQueryable();
return people;
}
Good if write query
https://localhost:7069/api/Persons?$top=5&$skip=0&$count=true
in browser it works I get results(5) as expected.
But for filters with query
https://localhost:7069/api/Persons?$filter=(LastName == null ? '' : LastName).ToLower().Contains('s'.ToLower())&$top=5&$skip=0&$count=true
I got this error :
{"Message":"The query specified in the URI is not valid. ')' or operator expected at position 10 in '(LastName == null ? '' : LastName).ToLower().Contains('d'.ToLower ())'.","ExceptionMessage":"')'
CodePudding user response:
On your API server:
[HttpGet]
[EnableQuery]
public ActionResult<IQueryable<Person>> Get()
{
IQueryable<Person> items = entityService.AsQueryable();
return Ok(items);
}
builder.Services.AddControllers().AddOData(options =>
{
options.AddRouteComponents(routePrefix: "odata", GetEdmModel())
.Select()
.Count()
.Filter()
.Expand()
.OrderBy()
.SetMaxTop(maxTopValue: 100);
});
public static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Person>(name: "Persons")
.EntityType.HasKey(person=> person.Id);
...
return builder.GetEdmModel();
}
public readonly struct ItemsPageResult<TItem>
{
public IEnumerable<TItem> Items { get; }
public int TotalItemsCount { get; }
public ItemsPageResult(IEnumerable<TItem> items, int totalItemsCount)
{
Items = items;
TotalItemsCount = totalItemsCount;
}
}
Your question shows your using a DTO which is fine. Be aware that your breaking a benefit of passing the query all the way back to the SQL engine if your using a provider that returns an IQueryable like EntityFramework does. If you want to map to a DTO, I suggest do it with an Expression
, the nuget package AutoMapper can do this for you.
In your client:
public async ValueTask<ItemsPageResult<ItemContextResult<TItem>>> GetItemsAsync(
int startIndex,
int count,
CancellationToken cancellationToken) => await GetItemsAsync(startIndex, count, queryString: "", cancellationToken);
public async ValueTask<ItemsPageResult<ItemContextResult<TItem>>> GetItemsAsync(
int startIndex,
int count,
string queryString,
CancellationToken cancellationToken)
{
var query = $"odata/{EndPoint}?{queryString}&$skip={startIndex}&$top={count}&$count=true";
OdataResponce<TItem> response = await HttpClient.GetContentAsync<OdataResponce<TItem>>(relativeUrl: query, cancellationToken);
var contextItems = response.Items.Select((item, index) => new ItemContextResult<TItem>(item, index startIndex)).ToList();
return new ItemsPageResult<ItemContextResult<TItem>>(contextItems, totalItemsCount: response.TotalItemsCount);
}
GetContentAsync
comes from RESTFulSense
change it to GetFromJsonAsync
for a plain HttpClient
.
public readonly struct ItemContextResult<TItem>
{
public TItem Item { get; }
public int Offset { get; }
public ItemContextResult(TItem item, int offset)
{
Item = item;
Offset = offset;
}
}
public class OdataResponce<TItem>
{
[JsonPropertyName(name: "@odata.context")]
public string ContextUrl { get; set; } = null!;
[JsonPropertyName(name: "@odata.count")]
public int TotalItemsCount { get; set; }
[JsonPropertyName(name: "value")]
public IEnumerable<TItem> Items { get; set; } = null!;
}
The ItemContextResult
is not required its a little extra I add to get the relative line number into the result.
In my case this passes the Query to EntityFramework...
The comes from a solution where im using:
<PackageReference Include="AutoMapper" Version="11.0.1" />
<PackageReference Include="Microsoft.AspNetCore.OData" Version="8.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.6" />
<PackageReference Include="RESTFulSense" Version="2.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.1" />
....
Client:
<PackageReference Include="RESTFulSense.WebAssembly" Version="1.0.0" />