Custom ReturnDTO and Filter Properties

I’m in the process of converting an oData API to AutoQuery and am not sure how to convert one of the calls. This call returns a MyClass object (shown below). Inflating the MyClass and returning it is straightforward, but I’m not sure how to hook into the magic of AutoQuery (e.g. only returning one of the collections, or even, better only querying the DB for one of the collections when that’s all that is requested).

public class MyClass 
{
    public List<StatusUpdate> StatusUpdates { get; set; }
    public List<ProductUpdate> ProductUpdates { get; set; }
    public List<PricingUpdate> PricingUpdates { get; set; }
    public List<FlavorUpdate> FlavorUpdates { get; set; }
}

[ServiceStack.Route("/MyClass")]
public class QueryMyClass  : QueryData<MyClass >
{
    public Guid MyId { get; set; }
}

public class MyClassServices : Service
{
    public IAutoQueryDb AutoQuery { get; set; }
    public async Task<MyClass > Any(QueryMyClass request)
    {

        MyClass result = new MyClass();

        var dbFactory = new OrmLiteConnectionFactory(WebConfigurationManager.ConnectionStrings["MyConnection"].ConnectionString, SqlServerDialect.Provider);

        using (var db = dbFactory.Open())
        {
            result.StatusUpdates = DB.Select<StatusUpdate>(x => x.Id == request.MyId);
            result.ProductUpdates = DB.Select<ProductUpdate>(x => x.Id == request.MyId);
            result.PricingUpdates = DB.Select<PricingUpdate>(x => x.Id == request.MyId);
            result.FlavorUpdates = DB.Select<FlavorUpdate>(x => x.Id == request.MyId);
        }

        return result;
    }
}

How can I have it work so that if the caller just wants to get the StatusUpdates, that’s all that gets returned? In oData they could specify which collection to expand with a query param and it took care of only returning that collection. To be clear, the MyClass object doesn’t correspond to any actual table - its a model created just for the API.

thanks!

Take a look at ShouldSerialize. Example:

  public class Order {
public string Id { get; set; }
...

public string ProcessingError { get; set; }
public OrderStatus Status { get; set; }
public bool? ShouldSerialize(string fieldName)
{
  if (ProcessingError == null) { return true; }
  if (this.Status != OrderStatus.processingError && fieldName.Equals("ProcessingError", StringComparison.OrdinalIgnoreCase))
  {
    return false;
  }
  return true;
}
}

You can explicitely allow serialization, based on the request.

I appreciate the response, but I honestly have no idea what that has to do with my question…
Perhaps if you showed how you would use that solve my problem it would help me understand.

the ShouldSerialize is called for every property of a DTO. If you return true in this specific method, the property is serialized to the output. If you return false, it will not include said property.
So, in case you would want to use this particulat DTO ‘MyClass’, you can decide which of the public List<> you wanted to return.

In my case, we only five the ProcessingError property back when the status of the Order is ‘processingError’.

In your case, the request could give back the StatusUpdates when the StatusUpdates contains any items, like this:

public bool? ShouldSerialize(string fieldName)
{
    if (fieldName.Equals("StatusUpdates", StringComparison.OrdinalIgnoreCase) && StatusUpdates != null){
      return  StatusUpdates.Count > 0;
    }    

    return true;
}

This method has to be added to the MyClass class.

Hope this helps.

But that seems to almost be manually doing the work AutoQuery is suppose to do for you. I’m asking how to hook into the AutoQuery magic.

Right. But if you create a DTO to hold all individual properties, you will get that returned.
If you wanted to get a specific list back and then apply the ShouldSerialize.
Another way would be to have a List of objects returned, filled with the specific items as a result.
Or, incude the type you need in the QueryMyClass, which in turn would allow you to only query on the specific DB table instead of all tables.

What are the options you had with oData?

In oData they could specify which collection to expand with a query param and it took care of only returning that collection

What did that look like?

Yes - I know this. I want to solve it through the AutoQuery framework. Your suggestion has nothing to do with AutoQuery as far as I can tell. In oData you can do /odata/MyClass?$expand=StatusUpdates and it handles the rest. AutoQuery was designed to do the same so I am confident it can support this without writing a custom serializer. I appreciate your efforts, but perhaps its best left for someone who can solve within AutoQuery.

Can anyone help with this? How can I have it so that AutoQuery is only executing and returning the specific collection/s that I need? I assume I need to use the Fields parameter that exists by default in AutoQuery, but not sure how to activate the magic with the scenario above since they are collections I am populating in memory.

thanks!

You can add Extend property to you DTO and check its value before filling resnose

[ServiceStack.Route("/MyClass")]
public class QueryMyClass  : QueryData<MyClass >
{
    public Guid MyId { get; set; }

    public string Extend { get; set; }

}

And in Service:


using (var db = dbFactory.Open())
{
    switch (request.Extend)
    {
        case "StatusUpdates":
            result.StatusUpdates = DB.Select<StatusUpdate>(x => x.Id == request.MyId);
            break;
        case "ProductUpdates":
                result.ProductUpdates = DB.Select<ProductUpdate>(x => x.Id == request.MyId);
            break;
        case "PricingUpdates":
                result.PricingUpdates = DB.Select<PricingUpdate>(x => x.Id == request.MyId);
                break;
        case "FlavorUpdates":
                result.FlavorUpdates = DB.Select<FlavorUpdate>(x => x.Id == request.MyId);
                break;
    }
}

That was my exact answer too :slight_smile:

For others with the same need, I believe this is the correct solution:

public async Task<MyClass > Any(QueryMyClass request)
{

    MyClass result = new MyClass();

        //only requesting and returning specific collections when they are specified
        if ((request.Fields != null) && (request.Fields.ToLower().Contains("statusupdates"))) { result.StatusUpdates = await Db.SelectAsync<StatusUpdate>(x => x.Id == request.MyId); }

this hooks into the existing Fields parameter and does not mess with serialization.