Making sense of large collection of data in Visual Studio

The thing about putting “Part 1” in the title of a blog post is that you sort of have to write at least a “Part 2” or people stop trusting you. So that’s where we are today: writing Part 2. In Part 1 we looked at searching through collections in the debugger. Turns out that the search tools in OzCode are available for more than just collections: we can also search in objects even when the object graph is very complex. Let’s see how that works.

Real-World Examples

I always like to give people real-world examples when exploring features in OzCode so they can see how truly applicable the features are. With complex object graphs, I had a little bit of trouble coming up with an idea. See the thing is that complex objects, large domain models and the such are difficult to work with and probably an anti-pattern. I didn’t want to show a bunch of terrible code so I had to search a little for a real-world complex object.

Enter Pulumi. Building complex infrastructure to host your applications used to involve writing vast deployment guides to hand over to some poor ops person who had to carefully follow the 12 pages of instructions. The rise of DevOps culture has highlighted what a bad practice that is and replaced it with repeatable infrastructure as code.

If you’ve ever had to build an ARM-template on Azure or a Cloud Formation template on AWS then you’ll know that it is an inelegant solution to the infrastructure as code problem. These files are a bunch of JSON which describes the resources on a cloud. If you want to be less tied to a particular cloud then you can use Terraform to create resources on a myriad of different clouds. However, out of the box, Terraform uses YAML another easy to mess up language.

Pulumi aims to change all of that. Pulumi mostly replaces the YAML and JSON with actual programming languages which allows for code completion, unit testing, and powerful parameterization. You can write your templates in TypeScript, JavaScript, or Python. C# (and one would assume other .NET languages) are in preview right now.

Let’s explore using OzCode to improve examining the large objects in Pulumi.

Getting that Debugger Attached

The .NET version of Pulumi is a runner program called pulumi.exe coupled with the .NET Core code you write. Because your library is dynamically loaded by Pulumi.exe it can be a bit of a challenge to attach a debugger in the first place.

Fortunately, this world is just full of helpful people like Mikhail who works at Pulumi.

A helpful tweet goes a long way

So I did just that.

static Task<int> Main()
{
    var breakNow = true; //trick compiler and avoids unreachable code warning
    while (breakNow)
    {
        Thread.Sleep(1000);
    }
...

The executable produced by my build is called “infra.exe” so I opened the attach to process dialog and searched for that process.

Attaching to this did indeed get me into the debugger and let me manually skip out of the loop.

A Large Object

There are some good examples of pretty complex deployment scenarios on the Pulumi github and I chose a slight variation of this one for setting up an app service. We have a pretty sizable object in the configuration for the AppService:

AppServiceArgs appServiceArgs = new AppServiceArgs
{
    ResourceGroupName = resourceGroup.Name,
    AppServicePlanId = appServicePlan.Id,
    HttpsOnly = true,
    Tags =
    {
        { "Owner", "Jane" },
        { "EmergencyContact", "Frank" },
        { "CostCenter", "Marketing" }
    },
    AppSettings =
    {
        { "WEBSITE_RUN_FROM_PACKAGE", codeBlobUrl },
        { "UserName", "bob" },
        { "StorageAccount", container.StorageAccountName },
        { "RemoteURL", "https://google.com" },
        { "MaxUsers", 12.ToString() },
        { "MinUsers", 4.ToString() }
    },
    ConnectionStrings =
    {
        new AppServiceConnectionStringsArgs
        {
            Name = "db",
            Type = "SQLAzure",
            Value = Output.Tuple<string, string, string>(sqlServer.Name, database.Name, password).Apply(t =>
            {
                (string server, string database, string pwd) = t;
                return $"Server= tcp:{server}.database.windows.net;initial catalog={database};userID={username};password={pwd};Min Pool Size=0;Max Pool Size=30;Persist Security Info=true;";
            }),
        },
        new AppServiceConnectionStringsArgs
        {
            Name = "db",
            Type = "SQLAzure",
            Value = Output.Tuple<string, string, string>(sqlServer2.Name, database2.Name, password).Apply(t =>
            {
                (string server, string database, string pwd) = t;
                return $"Server= tcp:{server}.database.windows.net;initial catalog={database};userID={username};password={pwd};Min Pool Size=0;Max Pool Size=30;Persist Security Info=true;";
            }),
        },
    },
};

If we debug that then we can see how searching inside a large object really shines. In this case, we want to find who owns a resource which is indicated by a tag. There could be dozens of tags on a resource so hunting through a buried collection will take time.

OzCode can search in objects, even several layers down to rapidly find exactly what we’re looking for: Jane owns this resource.

Searching inside a complex object

Just like in collections we can search deeper in the hierarchy by clicking on the chevrons.

Bonus

Now you’re through learning how to search in objects let me give you another cool tip. I don’t know if you’ve ever tried right-clicking on an object in the quick watch screen but there’s a world of cools stuff in there.

Quick watching showing search in objects

My favorite is the “Show All Instances of” because it immediately finds any other instances of an object. This is great when you need to hop about in a series of similar data structures.