DEV Community

Eros Fratini
Eros Fratini

Posted on

LINQPad: More control over your queries

Keep exploring

Following my previous post of this serie, I'll keep exploring the tool, to show you some useful trick you can use to get a better control of the query.

If you're an experienced LINQPad user a lot of this stuff will look trivial, but I actually used it for years without knowing them, and I think some of you will appreciate a kickstart.

Control the Dump

As you already noticed the result of the Dump() extension is varying from type to type. Simple types and strings, for example, are just dumped as HTML strings on the output panel.

Objects are dumped as HTML table with their properties exploded on rows:

While arrays, lists and other enumerable types are rendered as tables in which properties are exploded in columns:

The Dump() extension is automatically searching for public properties, recursively, nesting the results where is needed and rendering them is its standard way.

But if you look at the overloads of the method you'll see 5 parameters:

  • description
  • depth
  • toDataGrid
  • exclude
  • alpha

Writing something like:

DateTime.Now.Dump("Current date:");

Will use the description param, nesting the result under a label, useful if your query is outputting a lot of variables.

With the depth parameter you can control how much deep the Dump() is exploding the object on the first render. By default is 5, that means the Dump is preparing 5 levels of nested HTML tables upfront, and it can be a little too much, especially on large and complex objects (you will notice that the execution of the query will slow considerably if you try to render hundreds of results together). Setting the depth to a lower value will speed up the first render, and you'll still be able to manually explode the collapsed properties on the output panel just clicking on them.

Try for example:

System.Globalization.CultureInfo.GetCultures(System.Globalization.CultureTypes.AllCultures).Dump(1);

You'll notice a faster rendering, as this collection is quite big.

With the toDataGrid setted to true the output panel will switch to a WPF control instead of the standard HTML, while exclude allows you to omit from the rendering specific properties:

System.Globalization.CultureInfo.GetCultures(System.Globalization.CultureTypes.AllCultures).Dump(toDataGrid: true, exclude: "Parent");

Finally, the alpha params setted to true changes the order of appearence of the properties. By default LINQPad write them as they appear in the class, with this you'll see them alphabetically ordered.

Implementing ToDump()

You may need to specify a custom way to show data of specific classes, for example explicitating the properties to show or even the HTML format to use.

The fastest way is implementing a ToDump() method in the class, so assuming the class is defined in the query itself, you can write:

class User
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    object ToDump()
    {
        return new {
            FirstName = this.FirstName,
            LastName = Util.WithStyle(this.LastName, "color:green")
        };
    }
}

In this case ToDump() is returning an anonymous object with the same properties, but LastName will be rendered in green color (notice the use of Util, we'll talk more about later).

Implementing this kind of method in your domain model is not optimal (in this case impossible, Util is defined only in LINQPad queries), so an alternative would be to use a static method in the "My Extensions" query.
"My Extensions" is a special query you'll find in My Queries tab, that's always included during the execution of the other queries, so whatever you link or define there will be available everywhere.

Still not optimal: you'll have to move your User class in My Extensions (or reference there the assembly which contains the class). That, as said, will import the class in EVERY other query you will run.

In the end the best approach is working with reflection and dynamic keyword.

public static object ToDump(object input)
{
    if (input.GetType().FullName == "User") // e.g. "MyApp.DataModel.User"
    {
        dynamic output = input;

        return new
        {
            FirstName = output.FirstName,
            LastName = Util.WithStyle(output.LastName, "color:green")
        };
    }

    return input;
}

Another good approach, assuming you're producing your own NuGet packages, is to include in them a set of LINQPad example to be imported in your queries, as I'll show in future posts.

DumpContainer examples

A simple .Dump() invocation can be limiting in some scenario: each one is going to create a new dumped output row, in a sequence, without the ability to change the actual dumped result during the execution of the query. But if you need to show a progress or an updating information in general is not the good thing to use.

DumpContainer is going to help you.

As the name suggests is a sort of container for dynamically updated contents, you Dump() it once and update its Content as you need during the lifetime of the query:

void Main()
{
    DumpContainer dc = new DumpContainer();
    dc.Content = "Starting the application...";
    dc.Dump();

    Thread.Sleep(500);

    for (int i = 1; i <= 10; i++)
    {
        dc.Content = $"{i}/10 - Processing...";
        Thread.Sleep(200);
    }

    dc.Content = "Completed!";
}

If you just need a progress bar to be shown you can use, guess what, Util.ProgressBar.

As I said before, each LINQPad query's referencing LINQPad.Util namespace, an utility belt full of useful tools to interact with LINQPad itself, and ProgressBar is a class to work with simple but effective HTML progress bars.

We can replace the previous code with something like:

void Main()
{
    Util.ProgressBar pb = new LINQPad.Util.ProgressBar();
    pb.Caption = "Progress";
    pb.Dump();

    for (double i = 1; i <= 10; i++)
    {
        pb.Fraction = i/10;
        Thread.Sleep(200);
    }
}

In this case you can interact with the Fraction or Percent property to advance the bar.

Full progress bar

Manage input for interactive queries

In some scenario it can be useful to just not hardcode everything, but to ask for user input(s), in the same way you'll do with a console application.

Again you can access the Util namespace ad use the ReadLine() method. This will prompt an input box at the bottom of LINQPad window, its input can be assigned to a string variable and be used in the execution code:

Input example

Exploring the Util.*

Explore the Util namespace! Is really full of useful stuff that'll help you pushing your queries to the next level.

Some examples:

  • .Cmd() : Will execute any system command
  • .Dif() : A graphical comparer for objects, allows you to easily show differences between two given inputs
  • .KeepRunning() : creates a cycle to keep the query alive, useful when working with secondary threads
  • .CurrentQuery : is the object representing the current query, allows you to access the raw code and assembly references

Conclusions

LINQPad is really an amazing piece of software, knowing it well is going to improve by a factor of ten your productivity in the .NET ecosystem.

I'm going to write at least another post about it in the future, because there are still interesting stuff to talk about.

Stay tuned!

Top comments (2)

Collapse
 
jonasbarka profile image
Jonas Barkå • Edited

You have convinced me to get a LINQPad license!

I have tried the free version, but no autocomplete slows me down too much.

Looking forward to the next post! Maybe a comparison of the licenses?

Collapse
 
tanathos profile image
Eros Fratini

I never regretted my license, hope will be the same for you :D
For the next post I'm not sure yet... Maybe the NuGet and Visual Studio integrations...