Search code examples
c#salesforcesoql

How to get SOQL inner query as strongly-typed object (C#)


I'm new to Salesforce development, and trying to figure out SOQL stuff. Long time listener, first time caller.

I am running a SOQL query on ActivityHistories, and attempting to get it as a custom object. I need to do this so I can bind the data to an ASP.Net datagrid. Unfortunately, the dynamic type won't work for me, because I can't bind that to the grid.

Here is my query:

var activities = await client.QueryAsync<Activity>(@"SELECT (SELECT ActivityDate, Description 
FROM ActivityHistories
ORDER BY ActivityDate DESC NULLS LAST, LastModifiedDate DESC LIMIT 500)
FROM Account
WHERE Id = '" + SalesforceId + "' LIMIT 1");

And here's the custom data type I want to use

public class Activity
    {
        public DateTime ActivityDate { get; set; }
        public string Description { get; set; }
    }

When I run this request and bind the activities.records to the datagrid, I simply get no data. The headers for the columns appear, but I don't get any of the records that are supposed to be there. Even debugging doesn't provide any additional information, it just looks like I got a blank object back. However, when I run the same query and replace Activity with dynamic, I get a whole bunch of Json that does contain everything I'm looking for.

At first, I thought maybe the deserialization into my custom object was the problem, but I did a very similar thing with Opportunity, and it works just fine, automatically converting to that custom object. This leads me to believe I'm handling something with the inner query incorrectly, and I would greatly appreciate any direction.

Here's a short summary of stuff I have read and attempted:

Thank you in advance!


Solution

  • Under the hood, the Salesforce API is a collection of JSON services. The DeveloperForce toolkit (which you appear to be using) is not seeing a structure in your result object that matches the JSON object returned. This is because the nested query returns an object which has multiple levels of nested objects. Something like this:

    • Result of Query
      • Account
        • Result of Activity History Query
          • List of Activity History Records

    The serialization library has taken care of the first and second levels for you. You now have to provide an object to de-serialize into which can accommodate the third and fourth levels.

    I did some testing with similar code and found a hierarchy like this should work:

    public class ActivityQueryContainer
    {
        public Dictionary<string, string> Attributes { get; set; }
        public ActivityHistoryResult ActivityHistories { get; set; }
    }
    
    public class ActivityHistoryResult
    {
        public Activity[] Records { get; set; }
    }
    
    public class Activity
    {
        public DateTime ActivityDate { get; set; }
        public string Description { get; set; }
    }
    

    You can then change your API call to look like this:

    var activities = await client.QueryAsync<ActivityQueryContainer>(@"SELECT (SELECT 
        ActivityDate, Description FROM ActivityHistories ORDER BY ActivityDate DESC NULLS LAST, 
        LastModifiedDate DESC LIMIT 500) FROM Account WHERE Id = '" + SalesforceId + "' LIMIT 1");