How LINQPad Works
LINQPad is a client/server application with a twist. Whereas with most client/server
applications, there are many clients and one server, with LINQPad there is one client
and many servers!
The user interface is the client, as you might expect. For each query, LINQPad creates
a separate server, which a class that runs in its own process and executes
the query in isolation. This isolation prevents queries from interfering with each
other (or the UI) and allows LINQPad to safely cancel a query.
In the past, LINQPad used in-memory Remoting channels to communicate with the server processes. Now it
uses a custom-written communications layer that runs atop Windows shared memory (yes, there are plenty of pointers!)
This provides a faster, more reliable, and fully asynchronous communications layer.
LINQPad 5+ compiles your queries using the Microsoft Roslyn libraries.
Because C# and VB are statically typed, any database objects that you reference
need the backing of a typed DataContext. For performance, LINQPad builds typed DataContexts
on the fly - either using Reflection.Emit - or by generating and compiling source code.
Because so much work goes on the background (querying database schemas, emitting
typed DataContexts, compiling and then executing queries), every potentially
time-intensive feature
of LINQPad must operate asynchronously to maintain a responsive user interface.
LINQPad's Dump command feeds the output into an HTML stream which it
displays using an embedded web browser (you can see this by right-clicking a query
result and choosing 'Inspect'. The transformation into HTML is done
via a library called Hyperlinq. The deferred expansion of results and
lazy fetching of additional data when you click on hyperlinks works via JavaScript.
The lambda window populates using a custom expression tree visitor
(simply calling ToString on an expression tree is no good because it puts the entire
output on one line).
LINQPad's query editor uses Actipro's SyntaxEditor control
(a very polished product). Features such as syntax highlighting, red squiggly underlines and autocompletion
require that you lexically and semantically parse the source code. Lexical parsing
means reading the raw text stream and breaking it up into a stream of tokens; a
semantic parser then reads those tokens and figures out what they mean in context,
emitting a code DOM (called an Abstract Syntax Tree). The final step is to resolve
the nodes in the AST into .NET types and members (binding).
LINQPad 5+ uses the Microsoft 'Roslyn' libraries for parsing, binding, and compiling.
(Prior to this, the heavy lifting was done by NRefactory and SharpDevelop.Dom, written by
Daniel Grunwald, Andrea Paatz, and Mike Krüger, as part of the SharpDevelop project).
The debugger uses an adapted version of Microsoft's Managed Debugger sample. Two additional
layers sit on top of this, which are part of LINQPad. The first provides a lazy, asynchronous, cancellable, mostly immutable layer
which can self-heal references that have been moved by the GC. The second is a WPF View-Model, which synchronizes
with the layer below to provide bindable objects to display in the list views. The watch window expression evaluator uses
the NRefactory library.
Because LINQPad ships in just a handful of files, most of the libraries are
included as embedded resources. (Look at the last example
on this page to see how). A bonus of this approach is that you can compress the
libraries at the same time, reducing the assembly size.
LINQPad automatically patches itself by downloading updates into its Application
Data folder. It then checks that the new assembly has a valid signature, and if so,
forwards to that version, which then writes itself back to the original files.
This may fail (depending on the permissions of the folder to which LINQPad was downloaded), and so
LINQPad may request administrative elevation to complete the operation, and if this fails,
continues working by forwarding to the newer version in the Application Data folder.