0

Overview

I have a few GraphQL endpoints in different projects, and most of them use Entity Framework. We have a mix of HC 10 and HC 12 projects. In these we use HC filtering and it works great - the GraphQL queries are translated naturally into SQL queries. Also projections/selections are working lovely - no over fetching on rows or columns.

The issue

I have one endpoint though that doesn't use EF and instead talks to MS Graph using their .NET SDK (Microsoft.Graph.GraphServiceClient). Let's say my endpoint is to retrieve users from our instance. The way it was written before is like so:

[UseFiltering]
[UseSorting]
[Authorize]
public async Task<IEnumerable<Employee>> GetEmployees([Service] IMicrosoftDirectoryService directoryService)
    => (await directoryService.GetUsers()); 

Here's the GetUsers implementation - which is grabbing every user from MS.

public async Task<IEnumerable<User>> GetUsers(string filter = null)
{
    var users = new List<Microsoft.Graph.User>();
    var page = _client.Users.Request();
    

    if (!string.IsNullOrWhiteSpace(filter))
    {
        _ = page.Filter(filter);
    }

    while (page != null)
    {
        var result = await page.GetAsync();
        users.AddRange(result.CurrentPage);
        page = result.NextPageRequest;
    }

    return users;
}

So what happens present day is that if I want to.. filter by job title, my result set is correct - however we fetched every single user and then HC filtered out in memory.. Here's my query by the way:

{
    employees(where: { jobTitle: "Stackoverflow fiend" }) {
        displayName
        employeeNumber
        jobTitle
    }
}

What I can't figure out..

I've looked a lot of examples and documentation across HC 10-11-12, and can't find an elegant way to turn the HC filters into something Microsoft will understand (odata filter).

Right now here's my changes I made which work... just is messy. Here's the query resolver - I'm attempting to "build an odata filter" from HC's implementation..

[UseFiltering(FilterType = typeof(EmployeeFilterType))]
[UseSorting]
[Authorize]
public async Task<IEnumerable<Employee>> GetEmployees(
    [Service] IMicrosoftDirectoryService directoryService,
    [Service] IResolverContext resolverContext)
{
    // todo - custom filter?  this is messy..
    // anyway.. this is a list of all our WHERE conditions..
    var whereFilter = resolverContext.FieldSelection.Arguments.FirstOrDefault(a => a.Name.Value.ToLower() == "where");
    string filterString = null;

    if (whereFilter is not null)
    {
        var jobTitleFilter = ((List<HotChocolate.Language.ObjectFieldNode>)whereFilter.Value.Value)
            .FirstOrDefault(f => f.Name.Value == "jobTitle");
        if (jobTitleFilter is not null)
        {
            // this makes a string like "jobTitle eq 'foobar'"
            filterString = $"{jobTitleFilter.Name} eq '{jobTitleFilter.Value}'";
        }
    }

    return await directoryService.GetUsers(filterString);
}

Has anyone dealt w/ filtering or projections that aren't related to EF? I'm trying to find the correct way to ask HC "what are they asking for or filtering" or pie in the sky something exists which can translate these for me <3

Poat
  • 327
  • 1
  • 11

0 Answers0