Does anyone have a complete list of LINQPad extension methods and methods, such as
.Dump()
SubmitChanges()
Does anyone have a complete list of LINQPad extension methods and methods, such as
.Dump()
SubmitChanges()
Reached the StackOverflow text limit of 30000 characters in my previous answer, but there are still more cool extensions in LinqPad. Some of them I'd like to mention:
Automatically scroll to the end of the Results Window while a query is running
(using .Dump() statements):
Press Shift+Control+E to toggle (turn on or off)
Multiple entry points:
Press Alt+Shift+1 to run Main1(), Alt+Shift+2 to run Main2(), and so on.
Note that you still need void Main() as main entry point, the methods above are additional (optional) entry points.
Run xUnit tests:
Press Alt+Shift+T to run all the xUnit tests decorated with [Fact] or [Theory]
(as preparation you need to add xUnit support via Query -> Add xUnit test support menu)
This is not a LinqPad extension, but rather a .NET class, but since it is useful, I'll mention it anyway. You can get a lot of useful information you can use in your scripts such as :
Environment.UserDomainName.Dump();
Environment.MachineName.Dump();
Environment.UserName.Dump();
Environment.CurrentDirectory.Dump();
Environment.SystemDirectory.Dump();
N.B. For obtaining Domain\UserName I would use System.Security.Principal.WindowsIdentity.GetCurrent().Name
rather than Environment.UserDomainName+@"\"+Environment.UserName.
ListTables
Did you know you can write your own extensions in LinqPad, available in all queries? Here's how you can do it: In LinqPad, go to the "My Queries" tab on the left side, scroll down to the end until you see "My Extensions". Double click on it and it will open a special query window named My Extensions. What you write there will become available in all queries.
Now paste the following code into it, then save it with Ctrl+S:
My Extensions
void Main()
{
// no code here, but Main() must exist
}
public static class MyExtensions
{
/// <summary>
/// This will list the tables of the connected database
/// </summary>
public static IOrderedEnumerable<string> ListTables(
this System.Data.Linq.DataContext dc, bool dumpIt = true)
{
var query = dc.Mapping.GetTables();
var result = query.Select(t => t.TableName).OrderBy(o => o);
if (dumpIt) result.Dump();
return result;
}
}
Joe (the author of LinqPad) kindly provided me this snippet - it shows how you can pass the data context to My Extensions.
Note: For LinqPad 6 or greater, you need to press F4 for query properties and tick "Reference LINQ-to-SQL assemblies" to make it work.
Use this extension the following way: Open a new C# query window in LinqPad (with Ctrl+N), then connect to a database of your choice, and type:
New query
void Main()
{
this.ListTables();
}
Important: If you're not connected to a database, then the extension is not available and LinqPad will show an error. So, connect to a database first, then type this.ListTables();.
Note that IntelliSense will show the summary of the XML comment we typed in My Extensions. Once you run it, you will get a list of the tables of the current database.
Previously I have shown how to use MyExtensions. Now if you want either a global appsettings.json file or one per script, you can use the following extension:
public static class MyExtensions
{
// needs: Microsoft.Extensions.Configuration.json, press F4 and add it as NUGET package
public static IConfiguration AppSettings(string path = null)
{
IConfiguration config = null;
var configFile = (path != null) ? path : Util.CurrentQueryPath.Replace(".linq", ".appsettings.json");
if (System.IO.File.Exists(configFile))
{
var builder = new ConfigurationBuilder().AddJsonFile(configFile);
config = builder.Build();
}
else
{
configFile.Dump("Not found");
}
return config;
}
}
You can also store it directly in your C# program, but this way it's available per default and you need to do the NUGET loading only once.
Say you have written a LinqPad program "YourCSharpProgram.linq".
Now you can provide configuration like
var config1 = MyExtensions.AppSettings();
or like
var config2 = MyExtensions.AppSettings("C:\MyGlobalSettings\appsettings.json");
The first option, config1, will expect the the settings beneath the file "YourCSharpProgram.linq" and appends "appsettings.json" to it, meaning that your settings have to be in "YourCSharpProgram.linq.appsettings.json" in the same folder as the program.
The second option just uses the absolute path as specified.
If your settings file contains
{
"AzureStorage": {
"StorageConnectionString": "some connection string"
}
}
you can access it like
var config = MyExtensions.AppSettings();
string connectionString = config.GetSection("AzureStorage").GetSection("StorageConnectionString").Value.ToString();
connectionString.Dump();
NOTE: A second way to use configuration is to place the absolute path of your JSON file in LinqPads F4 dialog. In LinqPad 5, that was better because there was a separate tab for the settings file (there it was AppConfig, because version 5 is for .NET, not for .NET core). You have to reference it just as you would do it with an assembly, and it's not obvious. So I prefer it as described above.
Sometimes it is useful if you can click a button to perform an action on a particular row. You can do this by writing an Extension (which you could store in "My Extensions" as described earlier).
All of the methods below need to be inside of a static not nested extension class (e.g. public static class Ext { ... }).
First, we need to have an action button; to create one in a simple way we're using this helper method:
public static LINQPad.Controls.Button ActionButton<TSource>(this TSource arg,
string text, System.Action<TSource> selector)
=> new LINQPad.Controls.Button(text,
(LINQPad.Controls.Button b) => { selector(arg); });
The function takes a source type and allows to create a Button labled with a text and a selector (we'll see what that is in a minute).
With that, we can define a DumpAction method as follows:
public static void DumpAction<T>(this IEnumerable<T> lst, Action<T> selector,
string description = "", string clickText = "Click me")
{
lst.Select(val => new
{
val, click = val.ActionButton<T>(clickText, selector)
}).Dump(description);
}
This appends a column with buttons inside that you can click. You can use it as follows:
// list to display
var myList = new List<string>()
{
"Apple", "Pie"
};
All you have to do is:
myList.DumpAction((x) => myAction(x), "Click Extension");
Which will do essentially the same as you know from .Dump() method.
Now all it takes is to define a method myAction that is being called:
void myAction(string payload)
{
payload.Dump("myAction");
}
This method will be called if you click the button passing the string value of the list item to it.
Note: You can do the same for Dictionaries, and the extension above already works for them, but it is taking a lot of space in the results window for each row - to solve that, you can define a second extension method:
public static void DumpActionDict<K, V>(this IEnumerable<KeyValuePair<K, V>> lst,
Action<K> selector,
string description = "", string clickText = "Click me")
{
lst.Select(val => new
{
val.Key, val.Value, click = val.Key.ActionButton<K>(clickText, selector)
}).Dump(description);
}
Use it like:
var myDict = new Dictionary<string, string>()
{
["a"] = "x",
["b"] = "y"
};
myDict.DumpActionDict((x) => myAction(x), "Extension for dict");
Note: It is also possible to format the text inside the button differently, take a look at this example:
public static LINQPad.Controls.Button ActionButton<TSource>(this TSource arg, string text,
System.Action<TSource> selector)
{
var btn = new LINQPad.Controls.Button(onClick: (LINQPad.Controls.Button b) => { selector(arg); });
btn.HtmlElement.InnerHtml = $"<small>{text}</small>";
return btn;
}
That will make the button text smaller. You can use any HTML element(s).
You can create colored dumps by using Util.HighlightIf(condition, object) or Util.HighlightIf(condition, htmlcolor, object).
The following example, taken from LinqPad's release notes and colored it a bit more shows how:
void Main()
{
(from file in new DirectoryInfo(Util.LINQPadFolder).GetFiles()
select
Util.HighlightIf(file.Extension == ".txt", "lightblue",
Util.HighlightIf(file.Extension == ".json" || file.Extension == ".xml", "lightcyan",
Util.HighlightIf(file.Extension == ".cmd" || file.Extension == ".bat", "lightyellow",
Util.HighlightIf(file.Extension == ".dll", "lightgreen",
Util.HighlightIf(file.Extension == ".exe", // Highlight the entire row if the file is an executable.
new {file.Name,
Length=Util.HighlightIf(file.Length>999999,"orange",file.Length) ,
LastWriteDate=DateTime.Today.Date.ToString("yyyy-MM-dd")}
)))))).Dump();
}
Now, what does it do? It colors the cells based on
.bat,.txt, .json, .cmd, .dll, .xml and .exe have different colors for each of them (some share the same color).999999 bytes, its cell is colored in orange.This will create a dump like:
Sometimes it is useful to overwrite the text you dumped rather than putting it into a new line, for example if you're performing a long-running query and want to show its progress etc (see also ProgressBar below). This can be done by using a DumpContainer, you can use it as shown in the
Example 1:
void Main()
{
var dc = new DumpContainer("Doing something ... ").Dump("Some Action");
System.Threading.Thread.Sleep(3000); // wait 3 seconds
dc.Content += "Done.";
}
Note that for some more complex objects, you might have to use dc.UpdateContent(obj); rather than dc.Content=....
Example 2:
void Main()
{
var dc = new DumpContainer().Dump("Some Action");
for (int i = 10; i >= 0; i--)
{
dc.UpdateContent($"Countdown: {i}");
System.Threading.Thread.Sleep(250);
};
dc.UpdateContent("Ready for take off!");
}
Showing the progress can also be done by using a ProgressBar as follows:
Example:
void Main()
{
var prog = new Util.ProgressBar("Processing").Dump();
for (int i = 0; i < 101; i++)
{
Thread.Sleep(50); prog.Percent = i;
}
prog.Caption = "Done";
}
This is similar to the dump example before, but this time showing a nice progress bar animation.
.Dump())Since version 5.42 beta of LinqPad you can embed JavaScript functions and call them directly from your C# code. Although this has some limitations (compared with JSFiddle), it is a nice way to quickly test some JavaScript code in LinqPad.
Example:
void Main()
{
// JavaScript inside C#
var literal = new LINQPad.Controls.Literal("script",
@"function jsFoo(x) {
alert('jsFoo got parameter: ' + x);
var a = ['x', 'y', 'z']; external.log('Fetched \'' + a.pop() + '\' from Stack');
external.log('message from C#: \'' + x + '\'');
}");
// render & invoke
literal.Dump().HtmlElement.InvokeScript(true, "jsFoo", "testparam");
}
In this example, a function jsFoo with one parameter is prepared and stored in the variable literal. Then, it is rendered and called via .Dump().HtmlElement.InvokeScript(...), passing the parameter testparam.
The JavaScript function uses external.Log(...) to output text in LinqPad's output windows, and alert(...) to display a popup message.
You can simplify this by adding the following extension class/methods:
public static class ScriptExtension
{
public static object RunJavaScript(this LINQPad.Controls.Literal literal,
string jsFunction, params object[] p)
{
return literal.Dump().HtmlElement.InvokeScript(true, jsFunction, p);
}
public static LINQPad.Controls.Literal CreateJavaScript(string jsFunction)
{
return new LINQPad.Controls.Literal("script", jsFunction);
}
}
Then you can call the previous example as follows:
// JavaScript inside C#
var literal = ScriptExtension.CreateJavaScript(
@"function jsFoo(x) {
alert('jsFoo got parameter: ' + x);
var a = ['x', 'y', 'z']; external.log('Fetched \'' + a.pop() + '\' from Stack');
external.log('message from C#: \'' + x + '\'');
}");
// render & invoke
literal.RunJavaScript("jsFoo", "testparam");
That has the same effect, but is easier to read (if you intend to do more JavaScript ;-) ).
Another option, if you like Lambda expressions and you don't like to specify the function name as string each time you're calling it, you can do:
var jsFoo = ScriptExtension.CreateJavaScript(
@"function jsFoo(x) { ... }");
ScriptExtension.RunJavaScript(() => jsFoo, "testparam");
provided you've added the helper function
public static object RunJavaScript(Expression<Func<LINQPad.Controls.Literal>> expr,
params object[] p)
{
LINQPad.Controls.Literal exprValue = expr.Compile()();
string jsFunction = ((MemberExpression)expr.Body).Member.Name;
return exprValue.Dump().HtmlElement.InvokeScript(true, jsFunction, p);
}
to the class ScriptExtension. This will resolve the variable name you used (here jsFoo) which happens to be the same name as the JavaScript function itself (Note how the lambda expression is used to resolve the variable name, this cannot be done by using nameof(paramName) inside the function).
Did you know you can write unit tests in LinqPad? For example, you can use the xUnit framework. For version 5 of LinqPad, it is available through LinqPad's NUGET support - via F4 - in the dialog click Add NUGET..... And since version 6 of LinqPad, it is built-in (Menu Query -> Add XUnit test support). Here's a step-by-step description how to use xUnit with LinqPad V5, V6 or V7.
Example 1:
[Fact] void TestDb1()
{
var ctx = this; // "this" represents the database context
Assert.True(ctx.Categories.Any(), "No categories");
string.Join(", ", ctx.Categories.Select(s => s.CategoryName).ToList()).Dump("Categories");
Assert.True(ctx.Products.Any(), "No Products");
string.Join(", ", ctx.Products.Select(s => s.ProductName).ToList()).Dump("Products");
}
This example needs a Northwind sample database (set up as Linq to SQL or as Entity Framework Core Connection), assigned to the query, and XUnit Test support added (select Query -> Add XUnit test support in LinqPad). It will print all categories and products to the Results window. The test fails, if there are no categories or products in the database.
Example 2:
// for MemberData
public static IEnumerable<object[]> GetNumbers()
{
yield return new object[] { 5, 1, 3, 9 }; // 1st row: a, b, c, sum (succeeds)
yield return new object[] { 7, 0, 5, 13 }; // 2nd row: a, b, c, sum (fails)
}
[Theory]
[MemberData(nameof(GetNumbers))] // easier to handle than [ClassData(typeof(MyClass))]
void Test_Xunit(int a, int b, int c, int expectedSum)
=> Assert.Equal (expectedSum, a + b + c);
This example uses a [Theory] with the member function GetNumbers() to fill in the parameters a, b, c and expectedSum and gives you more flexibility than a [Fact]. A [Fact] cannot have any parameters. The test fails if the expected sum is not equal to the sum of a+b+c. This is the case for the 2nd row: The assert will output: (a: 7, b: 0, c: 5, expectedSum: 13).
Note: You can also use InlineData (e.g. [InlineData(1,2,3,6)]) multiple times for a theory passing the values directly, and you can even combine it with the MemberData attribute. It will add all values to the test and execute it.
The paid version of LinqPad (LinqPad 6 Premium) supports multiple databases in a query.
Below I am describing the steps for the database properies you get if you select "Linq to SQL (Optimized for SQL Server)".
Either create a new connection or open up an existing one. Open the Database Properties, select one database (don't use "Display all in a TreeView)
and then tick-mark "Include Additional Databases" - that will bring up another dialog where you can add multiple databases:
Click Pick from List... and you can choose + select another database. When you are done, click Close to close the additional dialog and then Ok to close the database properties.
Databases selected there are "secondary" contexts (listed with the database name in "this" UserQuery), the first database (which you selected under "Specify new or existing database") is a "primary" context (which means, the tables appear directly in "this" UserQuery).
In the connections window, this will be shown as
".\MyInstance\AdventureWorks2017 + AdventureWorks2017 + Northwind"
In the code below I am using "AdventureWorks2017" as primary context and "AdventureWorks2017" and "Northwind" as secondary contexts.
Prepared like this, you can do:
public UserQuery ctx => this; // context
void Main()
{
// "primary database"
ctx.Products.Select(s => new {s.ProductID, Name=s.Name}).Take(3).Dump("AdventureWorks");
// "secondary" databases
var aw = ctx.AdventureWorks2017;
var nw = ctx.Northwind;
nw.Products.Select(s => new {s.ProductID, Name=s.ProductName}).Take(3).Dump("Northwind");
aw.Products.Select(s => new {s.ProductID, Name=s.Name}).Take(3).Dump("AdventureWorks");
}
Both sample databases used in this example are from Microsoft, can be downloaded free, and they both have a Products table, but with different properties / fields: You can see that I've renamed the ProductName / Name so it appears in all queries as Name.
The program will give you the result:
Download links: AdventureWorks, Northwind, LinqPad