Home > Back-end >  How to configure OData and Radzen Blazor (client and server) for Net Core 6 and Odata 8
How to configure OData and Radzen Blazor (client and server) for Net Core 6 and Odata 8

Time:07-17

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" />
  • Related