Search code examples
c#asp.net-core.net-corejson-apijsonapi-serialize

JsonApiDotNetCore - How to filter by related object property?


Imagine I have two objects

public class OrderLine : Identifiable<Guid>
{
   [HasOne]
   public Order Order {get;set;}
   ...
   // other properties
}

public class Order : Identifiable<Guid>
{
   [Attr]
   public Guid CompanyId { get; set;}

   [HasMany]
   public ICollection<OrderLine> OrderLines {get; set;}
   ...
   // other properties
}

I want to perform an implicit filtering (independent of the filter created by the frontend). I want to filter the OrderLines by the CompanyId which is not directly on the object but is on the related parent object.

So I am implementing the OnApplyFilter(string existing filter), where I need the AttrAttribute to create an expression, etc. (as described on the JsonApiDotnetCore web page).

public override FilterExpression? OnApplyFilter(FilterExpression? existingFilter)
{
    // some initialization and Db fetching
    
    AttrAttribute companyIdAttribute = ResourceType.Attributes
            .Single(account => account.Property.Name == "Order.CompanyId"); 
            //This does not work and I am wondering what to do...
    // ...
    // some logic
    
    var exp = new ComparisonExpression(
           ComparisonOperator.Equals, 
           new ResourceFieldChainExpression(companyIdAttribute), 
           new LiteralConstantExpression("some company id which signed user has access to"))

    // rest of the method checking the filter and returning the appended `existingFilter` by my
    // custom filter
}


Solution

  • The attr lookup doesn't work because CompanyId has no [Attr] on it. But more importantly, what you're trying to do seems to be implementing multi-tenancy. There's a fully working implementation at https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenancyTests.cs. In that sample, country codes are used to distinguish web shops, similar to the companyID you're using. The sample is built in such a way that tenants are always separated, which applies to both read and write endpoints. At its core, it uses an EF Core query filter (see the DbContext class).