Search code examples
c#entity-framework-core.net-5query-stringmicrosoft-graph-sdks

Adding Microsoft Graph to existing Entity Framework Core


I got an API on .NET 5, made with Entity Framework Core.

The query-URLs with methods are something like GET https://api.mydomain.ch/v1/applicationusers

This would retrieve all application users (which makes sense).

In C# this looks:

[HttpGet]
public async Task<ActionResult<ApplicationUser>> Get()
{
    return Ok(await _applicationUserRepository.GetAll());
}

Now a client that consumes my API wants to request: GET https://api.mydomain.ch/v1/applicationusers?$filter=startswith(givenName,'Peter')

I have seen this matches the Microsoft Graph API and its query parameters. And my client also wants to use the other implementations for this (https://learn.microsoft.com/en-us/graph/query-parameters)

I couldn't find any implementation example that works with Entity Framework Core. Is it possible to implement this with an easy way, and not to make big modifications on the database?

Do I need to implement the functions completely on my own? Or is there support from Microsoft between those query parameter functions and Entity Framework Core?


Solution

  • Okay the answer is quite simple and it's not the Microsoft Graph that needs to be implemented, but simply OData.

    For .NET 5 it's not well documented, since the NuGet for this .NET-version is still in RC-state (pre-release).

    To implement OData, we need to add Microsoft.AspNetCore.OData in nuget packages. My answer is written with state from 8.0.0-rc. For v7.x the calls look bit different but are better documented.

    What we need to do is:

    Add OData in StartUp.ConfigureServices:

    ODataConventionModelBuilder builder = new(new DefaultAssemblyResolver());
    builder.EntitySet<ApplicationUser>("ApplicationUser");
    IEdmModel model =  builder.GetEdmModel();
    
    services.AddOData(opt =>
    {
        opt
            .AddModel("api/v1", model)
            .Select()
            .Expand()
            .OrderBy()
            .Filter()
            .Count();
    });
    

    with .Select().Expand().OrderBy().Filter().Count() you add the api-query-parameters for $select, $expand,...

    My UseEndpoints call in Configure() looks this way:

    app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
    

    Make sure, the controller inherit from ODataApiController. An action looks like this:

    [HttpGet]
    [EnableQuery]
    public async Task<ActionResult<ApplicationUser>> Get()
    {
        return Ok(await _applicationUserRepository.GetAll());
    }
    

    This would not execute the expected query. We need to return the IQueryable<T> to make this work, so we don't query for all records:

    [HttpGet]
    [EnableQuery]
    public IQueryable<ApplicationUser> Get()
    {
        return _applicationUserRepository.GetAll();
    }