LINQPad [扩展] 方法

发布于 2024-09-15 09:21:06 字数 1549 浏览 6 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

王权女流氓 2024-09-22 09:21:06

LINQPad 定义了两个扩展方法(在 LINQPad.Extensions 中),即 Dump()Disassemble()Dump() 使用 LINQPad 的输出格式化程序写入输出窗口,并重载以允许您指定标题:

typeof (int).Assembly.Dump ();
typeof (int).Assembly.Dump ("mscorlib");

您还可以指定最大递归深度以覆盖默认的 5 级:

typeof (int).Assembly.Dump (1);              // Dump just one level deep
typeof (int).Assembly.Dump (7);              // Dump 7 levels deep
typeof (int).Assembly.Dump ("mscorlib", 7);  // Dump 7 levels deep with heading

Disassemble() 反汇编任何方法到 IL,以字符串形式返回输出:

typeof (Uri).GetMethod ("GetHashCode").Disassemble().Dump();

除了这两个扩展方法之外,LINQPad.Util 中还有一些有用的静态方法。这些记录在自动完成中,包括:

  • Cmd - 执行 shell 命令或外部程序
  • CreateXhtmlWriter - 创建使用 LINQPad 的 Dump() 格式化程序的文本编写器
  • SqlOutputWriter< /strong> - 返回写入 SQL 输出窗口的文本编写器
  • GetMyQueriesGetSamples - 返回表示已保存查询/示例的对象集合(例如,执行使用“编辑 | 搜索全部”进行搜索)
  • 突出显示 - 包装对象,以便在转储时以黄色突出显示
  • Horizo​​ntalRun - 允许您在同一行上转储一系列对象

LINQPad还提供了HyperLinq 类。这有两个目的:第一个是显示普通超链接:

new Hyperlinq ("www.linqpad.net").Dump();
new Hyperlinq ("www.linqpad.net", "Web site").Dump();
new Hyperlinq ("mailto:[email protected]", "Email").Dump();

您可以将其与 Util.Horizo​​ntalRun 结合使用:

Util.HorizontalRun (true,
  "Check out",
   new Hyperlinq ("http://stackoverflow.com", "this site"),
  "for answers to programming questions.").Dump();

结果:

查看此网站以获取编程问题的答案。

HyperLinq 的第二个目的是动态构建查询:

// Dynamically build simple expression:
new Hyperlinq (QueryLanguage.Expression, "123 * 234").Dump();

// Dynamically build query:
new Hyperlinq (QueryLanguage.Expression, @"from c in Customers
where c.Name.Length > 3
select c.Name", "Click to run!").Dump();

您还可以编写您在 LINQPad 中自己的扩展方法。转到“我的查询”并单击名为“我的扩展”的查询。此处定义的任何类型/方法都可供所有查询访问:

void Main()
{
  "hello".Pascal().Dump();
}

public static class MyExtensions
{
  public static string Pascal (this string s)
  {
    return char.ToLower (s[0]) + s.Substring(1);
  }
}

在 4.46(.02) 中 新的类和方法已引入

  • DumpContainer(类)
  • OnDemand(扩展方法)
  • Util.ProgressBar(类)

此外,Hyperlinq 类现在支持 Action 委托,当您单击链接时将被调用,允许您在代码中对其做出反应,而不仅仅是链接到外部网页。

DumpContainer 是一个类,它将一个块添加到输出窗口中,该块可以替换其内容。

注意!请记住将 .Dump() DumpContainer 本身放在适当的位置。

使用方法:

var dc = new DumpContainer();
dc.Content = "Test";
// further down in the code
dc.Content = "Another test";

OnDemand是一个扩展方法,它不会将其参数的内容输出到输出窗口,而是添加一个可点击的链接,点击后将用.Dump替换该链接() 参数的内容。这对于有时需要的昂贵或占用大量空间的数据结构来说非常有用。

注意!请记住在适当的位置 .Dump() 调用 OnDemand 的结果。

要使用它:

Customers.OnDemand("Customers").Dump(); // description is optional

Util.ProgressBar 是一个可以在输出窗口内显示图形进度条的类,该进度条可以随着代码的继续而更改。

注意!请记住在适当的位置 .Dump() Util.ProgressBar 对象。

使用方法:

var pb = new Util.ProgressBar("Analyzing data");
pb.Dump();
for (int index = 0; index <= 100; index++)
{
    pb.Percent = index;
    Thread.Sleep(100);
}

LINQPad defines two extension methods (in LINQPad.Extensions), namely Dump() and Disassemble(). Dump() writes to the output window using LINQPad's output formatter and is overloaded to let you specify a heading:

typeof (int).Assembly.Dump ();
typeof (int).Assembly.Dump ("mscorlib");

You can also specify a maximum recursion depth to override the default of 5 levels:

typeof (int).Assembly.Dump (1);              // Dump just one level deep
typeof (int).Assembly.Dump (7);              // Dump 7 levels deep
typeof (int).Assembly.Dump ("mscorlib", 7);  // Dump 7 levels deep with heading

Disassemble() disassembles any method to IL, returning the output in a string:

typeof (Uri).GetMethod ("GetHashCode").Disassemble().Dump();

In addition to those two extension methods, there are some useful static methods in LINQPad.Util. These are documented in autocompletion, and include:

  • Cmd - executes a shell command or external program
  • CreateXhtmlWriter - creates a text writer that uses LINQPad's Dump() formatter
  • SqlOutputWriter - returns the text writer that writes to the SQL output window
  • GetMyQueries, GetSamples - returns a collection of objects representing your saved queries / samples (for an example, execute a search using Edit | Search All)
  • Highlight - wraps an object so that it will highlight in yellow when Dumped
  • HorizontalRun - lets you Dump a series of objects on the same line

LINQPad also provides the HyperLinq class. This has two purposes: the first is to display ordinary hyperlinks:

new Hyperlinq ("www.linqpad.net").Dump();
new Hyperlinq ("www.linqpad.net", "Web site").Dump();
new Hyperlinq ("mailto:[email protected]", "Email").Dump();

You can combine this with Util.HorizontalRun:

Util.HorizontalRun (true,
  "Check out",
   new Hyperlinq ("http://stackoverflow.com", "this site"),
  "for answers to programming questions.").Dump();

Result:

Check out this site for answers to programming questions.

The second purpose of HyperLinq is to dynamically build queries:

// Dynamically build simple expression:
new Hyperlinq (QueryLanguage.Expression, "123 * 234").Dump();

// Dynamically build query:
new Hyperlinq (QueryLanguage.Expression, @"from c in Customers
where c.Name.Length > 3
select c.Name", "Click to run!").Dump();

You can also write your own extension methods in LINQPad. Go to 'My Queries' and click the query called 'My Extensions'. Any types/methods that define here are accessible to all queries:

void Main()
{
  "hello".Pascal().Dump();
}

public static class MyExtensions
{
  public static string Pascal (this string s)
  {
    return char.ToLower (s[0]) + s.Substring(1);
  }
}

In 4.46(.02) new classes and methods have been introduced:

  • DumpContainer (class)
  • OnDemand (extension method)
  • Util.ProgressBar (class)

Additionally, the Hyperlinq class now supports an Action delegate that will be called when you click the link, allowing you to react to it in code and not just link to external webpages.

DumpContainer is a class that adds a block into the output window that can have its contents replaced.

NOTE! Remember to .Dump() the DumpContainer itself in the appropriate spot.

To use:

var dc = new DumpContainer();
dc.Content = "Test";
// further down in the code
dc.Content = "Another test";

OnDemand is an extension method that will not output the contents of its parameter to the output window, but instead add a clickable link, that when clicked will replace the link with the .Dump()ed contents of the parameter. This is great for sometimes-needed data structures that is costly or takes up a lot of space.

NOTE! Remember to .Dump() the results of calling OnDemand in the appropriate spot.

To use it:

Customers.OnDemand("Customers").Dump(); // description is optional

Util.ProgressBar is a class that can show a graphical progressbar inside the output window, that can be changed as the code moves on.

NOTE! Remember to .Dump() the Util.ProgressBar object in the appropriate spot.

To use it:

var pb = new Util.ProgressBar("Analyzing data");
pb.Dump();
for (int index = 0; index <= 100; index++)
{
    pb.Percent = index;
    Thread.Sleep(100);
}
来世叙缘 2024-09-22 09:21:06

LinqPad 扩展方法  - 第 1 部分(共 2 部分)

转到第 2 部分


除了众所周知的 myQuery.Dump (“查询结果:”),另一个值得一提的功能是 Util 类:它包含许多非常方便的方法(其中一些我已经提到过,但还有更多) 。

同样有趣的是,您可以修改Dump()的工作方式

最后,我将向您展示如何使用 SubmitChanges()永久更改(即插入、更新、删除 LINQ 查询) >SaveChanges() 以及如何访问 LinqPad 的内部连接对象。

总而言之,我将向您展示如何在 LinqPad 内创建简单的二维图形(绘制线条、位图函数)。

因此,这里有一系列内置 LinqPad 功能(来自我自己使用该工具的经验):


.Dump()

(LinqPad v5.03.08 及更高版本中提供的参数)

.Dump() 扩展方法消耗并打印(几乎)所有内容。

但您知道有几个可用参数吗?
看一下这段代码片段:

var obj=new { a="Hello", b=5, c="World", d=new { y=5, z=10 } };
obj.Dump(description: "1st example", depth: 5, toDataGrid: false, exclude: "b,d");
obj.Dump("2nd example", exclude: "a,c");
obj.Dump("2nd example", exclude: "+b,d"); // new in V5.06.06 beta

第一个示例仅打印变量 ac 并隐藏 b 和 < code>d,第二个示例执行相反的操作(请注意,它仅指定 2 个可用参数)。变量yz无法单独隐藏,因为它们不在顶层。

以下参数可用(全部都是可选的):

  • description [string] - 提供要转储的对象的描述
  • 深度 [int?] - 限制递归检查对象的深度
  • toDataGrid< /code> [bool] - 如果为 true,输出将格式化为数据网格而不是 RichText
  • exclude [string] - 如果您提供以逗号分隔的变量列表,它们将从输出(在示例“a,c”中:显示 bd,隐藏 ac )
  • exclude [string] 带有“+”前缀 - 该前缀反转了排除参数的逻辑。这意味着,如果您提供以逗号分隔的变量列表,则除了指定的变量之外的所有变量都将被隐藏(在示例“+b,d”中:bd是显示,所有其他隐藏)
  • 将包含和排除的属性存储在变量中(自 LinqPad V5.09.04 起新增):
    var x=Util.ToExpando(obj, "a, c", "b, d") ; x.Dump();
    第一个字符串包含要包含的属性列表,第二个字符串包含要排除的列表,
  • 单击时展开:如果使用 .OnDemand("click me")。 Dump(); 而不是 .Dump(),它将显示一个可以单击展开的链接。如果您想检查值,例如 Util.OnDemand("Customer-ID: " + customerObject.ID.ToString(), ()=>customerObject, false).Dump(); 到始终默认显示 ID,但仅在您感兴趣时才显示 customerObject 的详细信息。

可以找到有关 Dump 的更多高级主题
此处(自定义转储)此处(扩展第 2 部分)< /a>.


Util.WriteCsv

新功能:LinqPad 版本 v4.45.05(测试版))

Util.WriteCsv (Customers, @"c:\temp\customers.csv");

这会将表 Customers 的内容写入 CSV 文件 c:\temp\customers.csv。您还可以找到一个很好的示例,说明如何使用 Util.WriteCsv,然后在 Linqpad 的结果窗口中显示 CSV 数据此处

提示:

  • 要获取/创建与查询位于同一目录中的 CSV 文件,您可以使用:
    var csvFile=Util.CurrentQueryPath.Replace(".linq", ".csv");

  • 如果表很大,请先使用 ObjectTrackingEnabled = false;写入 CSV 以避免将其缓存在内存中。

  • 如果您想以 XML 格式输出表格而不是逗号分隔的文件,您可以这样做:

     var xmlFile=Util.CurrentQueryPath.Replace(".linq", ".xml");
      var xml = XElement.Load(xmlFile);
      变量查询 =
        来自 xml.Elements() 中的 e
        其中 e.Attribute("attr1").Value == "a"
        选择e;
      查询.转储();
    

此示例返回具有属性 attr1 的所有元素,该属性包含来自 XML 文件的值 "a",该文件与查询同名并且包含在同一路径中。查看链接以获取更多代码示例。


Util.GetPassword

var pwd = Util.GetPassword("UserXY");

这将从 LinqPad 的内置密码管理器中检索密码。创建&更改密码,打开 LinqPad 的“文件”菜单中的“密码管理器”菜单项。如果运行 C# 代码时没有保存密码,则会打开一个密码对话框,要求您输入密码,您可以通过选中保存密码复选框来选择动态创建和保存密码(在示例中,“UserXY”的密码将被保存,稍后您可以在密码管理器中找到此条目)。

优点是您可以将密码存储在您安全地、单独创建并在 Windows 用户配置文件中加密的 LinqScript 中(它作为文件存储在 %localappdata%\LINQPad\Passwords 中)。 LinqPad 使用 Windows DPAPI 来保护密码。

此外,密码是集中存储的,因此如果您需要更改密码,您可以在菜单中进行更改,它会立即应用于您创建的所有脚本。

注意:

  • 如果您不想保存密码而只想弹出密码对话框,可以使用第二个参数,如下所示:
    var pwd = Util.GetPassword("UserXY", true);
    这将取消选中密码对话框中的保存密码复选框(但是,用户仍然可以选中它并选择保存)。

  • 如果您需要将密码存储在 SecureString 中,则可以使用此辅助函数(注意:要获取使用的扩展方法 .ToSecureString(),请请关注 Stackoverflow 上的此链接 - 如果需要,它还允许您将其转换回来):
    System.Security.SecureString GetPasswordSecure(string Name, bool noDefaultSave=true)
    <代码>{
    return Util.GetPassword(Name, noDefaultSave).ToSecureString();
    }



Util.Cmd

此方法的工作方式类似于命令处理器。您可以从 Windows 控制台调用您知道的所有命令。

示例 1 - dir:

Util.Cmd(@"dir C:\");

这将输出目录的结果,而无需 .Dump 它。将其存储在变量中的优点是您可以对其使用进一步的 Linq 查询。例如:

var path=@"C:\windows\system32"; 
var dirSwitch="/s/b";
var x=Util.Cmd(String.Format(@"dir ""{0}"" {1}", path, dirSwitch), true);
var q=from d in x 
        where d.Contains(".exe") || d.Contains(".dll")              
        orderby d
    select d;
q.Dump();

这将转储 C:\windows\system32 中包含的所有文件扩展名为“.exe”或“.dll”的文件。 /s 开关用于递归所有子目录,/b 用于裸输出格式。请注意,Cmd 方法的第二个参数被指定为抑制控制台输出,以便仅显示使用 Dump 方法过滤的结果。

您可以看到,这比 dir 中的通配符更灵活,因为您可以充分利用 Linq 查询引擎的灵活性。

示例 2 - 文本编辑器:

您可以像这样在记事本中打开文件:

var filePath=@"C:\HelloWorld.txt";
Util.Cmd(@"%systemroot%\system32\notepad.exe", filePath);

Util.Image

显示来自 URL 的图像。示例:

var url = "http://chart.apis.google.com/chart?cht=p3&chd=s:Uf9a&chs=350x140&chl=January|February|March|April";
Util.Image(url).Dump();

Util.ProgressBar、Util.Progress

使用Util.ProgressBar 允许您显示进度条。您可以使用以下辅助类:

public class ProgressBar
{
    Util.ProgressBar prog;
    
    public ProgressBar() 
    { 
        Init("Processing"); 
    }
    
    private void Init(string msg)
    {
        prog = new Util.ProgressBar (msg).Dump();
        prog.Percent=0;
    }

    public void Update(int percent)
    {
        Update(percent, null);
    }   
    
    public void Update(int percent, string msg)
    {
        prog.Percent=percent;
        if (String.IsNullOrEmpty(msg))
        {
            if (percent>99) prog.Caption="Done.";
        }
        else
        {
            prog.Caption=msg;
        }
    }
}

只需按照以下示例所示使用它:

void Main()
{
    var pb1= new ProgressBar();
    Thread.Sleep(50);
    pb1.Update(50, "Doing something"); Thread.Sleep(550);
    pb1.Update(100); Thread.Sleep(50);
}

您也可以使用 Util.Progress 来更新 LinqPads 集成进度条,例如:

Util.Progress = 25; // 25 percent complete

不同之处在于它不会显示在结果窗口,并且您无法为其分配消息。


Util.RawHtml

在输出窗口中显示 HTML。示例:

Util.RawHtml (new XElement ("h1", "This is a big heading")).Dump();

Hyperlinq、Util.Horizo​​ntalRun

您可以使用此示例函数

public void ShowUrl(string strURL, string Title)
{
    Action showURL = delegate() { Process.Start("iexplore.exe", strURL); };
    var url = new Hyperlinq(showURL, "this link", true);
    Util.HorizontalRun (true, "Click ", url, " for details.").Dump(Title);
}

在结果窗口中显示超链接 - 或任何操作,例如打开您喜欢的编辑器。
用法:

ShowUrl("http://stackoverflow.com", "Check out StackOverflow");

注意这个函数始终有效,而new Hyperlinq("http://myURL", "Web site").Dump(); 不适用于某些类型的 URL(特别是,如果您必须传递“:1234”等端口名称作为 URL 的一部分)。


Util.ReadLine

从控制台读取输入。示例:

int age = Util.ReadLine<int> ("Enter your age");

作为 Util.ReadLine() 的同义词,您也可以使用 Console.ReadLine()

但还有更多!您可以使用以下代码片段创建一个简单的 JSON 解析器 - 非常有用,例如,如果您想动态解析和测试 JSON 字符串。 使用文本编辑器将以下代码段保存为 JSONAnalyzer.linq,然后在 LinqPad 中打开它(这是为了轻松地动态添加引用):

<Query Kind="Program">
    <Reference><RuntimeDirectory>\System.Web.Extensions.dll</Reference>
    <Namespace>System.Web.Script.Serialization</Namespace>
</Query>

void Main()
{
    var jsonData=Util.ReadLine<string>("Enter JSON string:");
    var jsonAsObject = new JavaScriptSerializer().Deserialize<object>(jsonData);
    jsonAsObject.Dump("Deserialized JSON");
}

现在您可以运行它只需将剪贴板中的 JSON 字符串粘贴到控制台中 - 它将使用 Dump 函数将其很好地显示为对象 - 并且您还可以在屏幕上看到解析器的错误消息来修复问题。对于调试 AJAX 非常有用。

JSON


Util.ClearResults

如果您需要清除脚本内的结果窗口,请使用:

Util.ClearResults();

在脚本顶部使用它,或者 - 如果您在脚本中运行多个查询 - 您应该等待用户输入在清空屏幕之前(例如,在其前面添加 Util.ReadLine)。


自定义 .Dump() - ICustomMemberProvider

同样有趣的是,您可以更改 .Dump() 方法的输出。只需实现接口ICustomMemberProvider,例如,

public class test : ICustomMemberProvider 
{

      IEnumerable<string> ICustomMemberProvider.GetNames() {
        return new List<string>{"Hint", "constMember1", "constMember2", "myprop"};
      }
      
      IEnumerable<Type> ICustomMemberProvider.GetTypes() 
      {
        return new List<Type>{typeof(string), typeof(string[]), 
            typeof(string), typeof(string)};
      }
      
      IEnumerable<object> ICustomMemberProvider.GetValues() 
      {
        return new List<object>{
        "This class contains custom properties for .Dump()", 
        new string[]{"A", "B", "C"}, "blabla", abc};
      }

      public string abc = "Hello1"; // abc is shown as "myprop"
      public string xyz = "Hello2"; // xyz is entirely hidden
}

如果您创建此类的实例,

var obj1 = new test();
obj1.Dump("Test");

那么它将仅输出HintconstMember1constMember2myprop,但不是属性 xyz

 Linqpad dump


在 LinqPad 中显示消息框或输入框

如果您需要显示消息框,请查看此处如何操作。

例如,您可以使用以下代码显示一个InputBox

void Main()
{
    string inputValue="John Doe"; 
    inputValue=Interaction.InputBox("Enter user name", "Query", inputValue);
    if (!string.IsNullOrEmpty(inputValue)) // not cancelled and value entered
    {
        inputValue.Dump("You have entered;"); // either display it in results window
        Interaction.MsgBox(inputValue, MsgBoxStyle.OkOnly, "Result"); // or as MsgBox
    }
}

(不要忘记按F4并添加Microsoft.VisualBasic.dll及其命名空间以使其工作)


Util.Run

新:可用自 LinqPad 版本 v4.52.1(测试版) 起,

允许您从脚本中运行另一个 LINQPad 脚本或者在您自己的 .NET 程序或 Windows 服务中(通过引用 LINQPad.exe 的 LINQPad4-AnyCPU 版本)。它执行脚本的方式与命令行工具 lprun.exe 的执行方式相同。

示例

const string path=@"C:\myScripts\LinqPad\";
var dummy=new LINQPad.QueryResultFormat(); // needed to call Util.Run
Util.Run(path+"foo.linq", dummy);

此示例运行脚本 foo.linq,其中包含以下示例代码:

void Main(string[] args)
{
    #if CMD
       "I'm been called from lprun! (command line)".Dump();
    #else
       "I'm running in the LINQPad GUI!".Dump();
       args = new[] { "testhost", "[email protected]", "[email protected]", "Test Subject" };
    #endif
    args.Dump("Args");
}

它允许您检查脚本是从 LinqPad GUI 内部运行还是通过 < code>lprun.exe 或使用 Util.Run

注意:以下调用变体可能会有帮助:

Util.Run(path+"foo.linq", dummy).Dump(); // obviously dumps the script output!
Util.Run(path+"foo.linq", dummy).Save(path+"foo.log"); // writes output into log
Util.Run(path+"foo.linq", dummy).SaveAsync(path+"foo1.log");     // async output log

SubmitChanges() - Linq To SQL

如果您使用 LinqToSQL,您可能希望使更改永久化(对于 插入/更新/删除操作)。
由于数据库上下文是由 LinqPad 隐式创建的,因此您需要在每次更改后调用 SubmitChanges(),如下所示。

(LinqPad-)Northwind 数据库示例:

插入

var newP = new Products() { ProductID=pID, CategoryID=cID, 
            ProductName="Salmon#"+pID.ToString() };
Products.InsertOnSubmit(newP);
SubmitChanges();    

更新

var prod=(from p in Products
            where p.ProductName.Contains("Salmon")
            select p).FirstOrDefault();
prod.ProductName="Trout#"+prod.ProductID.ToString();
SubmitChanges(); 

删除

var itemsToDelete=Products.Where(p=> p.ProductName.Contains("Salmon") ||
    p.ProductName.Contains("Trout"));
foreach(var item in itemsToDelete) { Products.DeleteOnSubmit(item); }
SubmitChanges();

注意:为了获取前面示例的有效 ID,您可以

var cID = (from c in Categories 
            where c.CategoryName.Contains("Seafood") 
            select c).FirstOrDefault().CategoryID;

var pID = Products.Count()+1;

在调用它们之前使用:。


SaveChanges() - 实体框架

如果您使用实体框架,您可能还希望使更改永久化(对于插入/更新/删除操作)。
由于数据库上下文是由 LinqPad 隐式创建的,因此您需要在每次更改后调用 SaveChanges(),如下所示。

这些示例与之前的 LinqToSQL 基本相同,但您需要使用 SaveChanges() 来代替,并且插入和删除的方法也发生了变化。

插入

var newP = new Products() { ProductID=pID, CategoryID=cID, 
            ProductName="Salmon#"+pID.ToString() };
Products.Add(newP);
SaveChanges();  

更新

var prod=(from p in Products
            where p.ProductName.Contains("Salmon")
            select p).FirstOrDefault();
prod.ProductName="Trout#"+prod.ProductID.ToString();
SaveChanges(); 

删除

var itemsToDelete=Products.Where(p=> p.ProductName.Contains("Salmon") ||
    p.ProductName.Contains("Trout"));
foreach(var item in itemsToDelete) { Products.Remove(item); }
SaveChanges();

注意:为了获得前面示例的有效ID,您可以

var cID = (from c in Categories 
            where c.CategoryName.Contains("Seafood") 
            select c).FirstOrDefault().CategoryID;

var pID = Products.Count()+1;

在之前 使用:调用它们。

如果您需要进行事务,请查看这篇文章:如何嵌套事务


this - 数据库上下文

LinqPad 中,通过使用顶部的组合框并为查询选择正确的数据库,会自动应用数据库上下文
但有时,显式引用它很有用,例如,如果您从 Visual Studio 中复制项目中的一些代码,并将其粘贴到 LinqPad 中。

从 Visual Studio 项目中获取的代码片段很可能如下所示:

var prod=(from p in dc.Products
            where p.ProductName.Contains("Salmon")
            select p).FirstOrDefault();
prod.ProductName="Trout#"+prod.ProductID.ToString();
dc.SaveChanges(); 

现在如何处理 dc?当然,您可以删除查询中出现的每个 dc.,但这要容易得多。
只需将其添加到代码片段的顶部,如下所示:

UserQuery dc { get => this; }
void Main()
{
    var prod=(from p in dc.Products
                where p.ProductName.Contains("Salmon")
                select p).FirstOrDefault();
    prod.ProductName="Trout#"+prod.ProductID.ToString();
    dc.SaveChanges(); 
}   

代码将立即运行!


this.Connection

将 LinqPad 与 OleDb 结合使用,将数据表转换为 Linq 对象,在 Linq 中进行 SQL 查询

以下代码片段可帮助您将 LinqPad 与 OleDb 结合使用。将 System.Data 程序集中的 System.Data.OleDb 添加到查询属性,然后将以下代码粘贴到 Main() 中:

var connStr="Provider=SQLOLEDB.1;"+this.Connection.ConnectionString; 

OleDbConnection conn = new OleDbConnection(connStr);
DataSet myDS = new DataSet();
conn.Open();

string sql = @"SELECT * from Customers";
OleDbDataAdapter adpt = new OleDbDataAdapter();
adpt.SelectCommand = new OleDbCommand(sql, conn); 
adpt.Fill(myDS);

myDS.Dump();

现在添加与 LinqPad 的 SqlServer 连接并添加 Northwind 数据库以运行此示例。

注意:如果您只想获取当前所选连接的数据库和服务器,您可以使用以下代码片段:

void Main()
{
    var dc=this;
    var tgtSrv=dc.Connection.DataSource;
    var tgtDb=dc.Connection.ConnectionString.Split(';').Select(s=>s.Trim())
        .Where(x=>x.StartsWith("initial catalog", StringComparison.InvariantCultureIgnoreCase))
        .ToArray()[0].Split('=')[1];
    tgtSrv.Dump();
    tgtDb.Dump();
}

您甚至可以将 myDS 转换为 Linq, 以下问题的答案展示了如何执行此操作:将 .NET 4 动态关键字与 Linq 结合使用的好示例

再举一个例子:假设您的 DBA 给您一个 SQL 查询,并且您想要在 LinqPad 中分析结果 - 当然是在 Linq 中,而不是在 SQL 中。您可以执行以下操作:

void Main()
{
    var dc=this;
    
    // do the SQL query
    var cmd =
        "SELECT Orders.OrderID, Orders.CustomerID, Customers.CompanyName,"
        +"       Customers.Address, Customers.City"
        +" FROM Customers INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID";
    var results = dc.ExecuteQuery<OrderResult>(cmd);
    
    // just get the cities back, ordered ascending
    results.Select(x=>x.City).Distinct().OrderBy(x=>x).Dump();
}

class OrderResult
{   // put here all the fields you're returning from the SELECT
    public dynamic OrderID=null; 
    public dynamic CustomerID=null;
    public dynamic CompanyName=null;
    public dynamic Address=null;
    public dynamic City=null;
}

在此示例中,DBA 的 SELECT 查询只是“放入”命令文本中,并且结果按城市进行过滤和排序。
当然,这是一个简化的示例,您的 DBA 可能会给您一个更复杂的脚本,但您的想法是:添加一个支持结果类,其中包含 SELECT 子句中的所有字段,然后您可以直接使用它。
您甚至可以通过这种方式从存储过程中获取结果并在 Linq 中使用它。正如您所看到的,在这个示例中我不关心数据类型并使用dynamic来表达它。
所以这实际上是关于快速编程,以便能够快速分析数据。由于各种原因(SQL 注入,因为您可以从一开始就使用 EF 等),您不应该在实际应用程序中执行此操作。


PanelManager

在 LinqPad 中绘制图形,第 1 部分

要使用下面的示例,请按 F4 并添加 System.Windows.dllSystem.dll。 Windows.Forms.dllWindowsFormsIntegration.dllPresentationCore.dllPresentationFramework.dll 到您的 LinqPad 程序中,并添加命名空间System.Windows.Shapes

第一个示例只是画一条线:

var myLine = new Line();
myLine.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
myLine.X1 = 1; myLine.X2 = 50;
myLine.Y1 = 1; myLine.Y2 = 50;
myLine.StrokeThickness = 2;
PanelManager.DisplayWpfElement(myLine, "Graphic");

第二个示例展示了如何使用PanelManager在LinqPad中显示图形。通常 LinqPad 仅支持 Wpf 对象。此示例使用 System.Windows.Forms.Integration.WindowsFormsHost 使 PictureBox 可用(其灵感来自于 这个):

// needs (F4): System.Windows.dll, System.Windows.Forms.dll, 
// WindowsFormsIntegration.dll, PresentationCore.dll, PresentationFramework.dll 
void Main()
{       
    var wfHost1 = new System.Windows.Forms.Integration.WindowsFormsHost();
    wfHost1.Height=175; wfHost1.Width=175; wfHost1.Name="Picturebox1";
    wfHost1.HorizontalAlignment=System.Windows.HorizontalAlignment.Left;
    wfHost1.VerticalAlignment=System.Windows.VerticalAlignment.Top;
    System.Windows.Forms.PictureBox pBox1 = new System.Windows.Forms.PictureBox();
    wfHost1.Child = pBox1;
    pBox1.Paint += new System.Windows.Forms.PaintEventHandler(picturebox1_Paint);
    PanelManager.StackWpfElement(wfHost1, "Picture");
} 

public string pathImg
{
    get { return System.IO.Path.Combine(@"C:\Users\Public\Pictures\Sample Pictures\", 
            "Tulips.jpg"); } 
}

// Define other methods and classes here
public void picturebox1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
    // https://stackoverflow.com/a/14143574/1016343
    System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(pathImg);
    System.Drawing.Point ulPoint = new System.Drawing.Point(0, 0);
    e.Graphics.DrawImage(bmp, ulPoint.X, ulPoint.Y, 175, 175);
}

这将创建以下图形(面板项“图形”和“图片”是由上面的示例添加的):

Showing_Graphic_in_LinqPad

如果您想显示来自 Northwind 数据库的图像,您可以执行以下操作:
将图像文件名更改为“NorthwindPics.jpg”,然后在第二个示例 Main() 方法的开头添加以下代码:

var img = (from e in this.Employees select e).FirstOrDefault().Photo.ToArray();
using (FileStream fs1 = new FileStream(pathImg, FileMode.Create))
{
    const int offset=78;
    fs1.Write(img, offset, img.Length-offset);
    fs1.Close();
}

它将从Employees 表中读取第一条记录并显示图片。

查看以下链接以了解更多信息:
WPF 中的形状和基本绘图
LinqPad 自定义可视化工具

注意: 您可以在没有 PanelManager 的情况下实现相同的效果,如下所示好吧,如下例,我在此处看到显示:

// using System.Drawing;
using (var image=new Bitmap(100, 100))
using (var gr = Graphics.FromImage(image))
{
    gr.FillRectangle(Brushes.Gold, 0, 0, 100, 100);
    gr.DrawEllipse(Pens.Blue, 5, 5, 90, 90);
    gr.Save();
    image.Dump();
}

它正在使用 .Dump() 命令来显示它。您可以多次调用image.Dump(),它将附加图像。


Windows 窗体

在 LinqPad 中绘制图形,第 2 部分

以下示例的灵感来自 这篇 文章展示了如何使用 C#7 在 Linqpad 中实现函数绘图仪:

void Main()
{
    fnPlotter(x1: -1, x2: 1, fn: (double x) => Math.Pow(x, 3)).Dump();
}

public static Bitmap fnPlotter(double x1=-3, double x2=3, double s=0.05, 
                                   double? ymin=null, double? ymax=null, 
                                   Func<double, double> fn = null, bool enable3D=true)
{
    ymin = ymin ?? x1; ymax = ymax ?? x2;
    
    dynamic fArrPair(double p_x1 = -3, double p_x2 = 3, double p_s = 0.01, 
                          Func<double, double> p_fn = null)
    {
        if (p_fn == null) p_fn = ((xf) => { return xf; }); // identity as default
        var xl = new List<double>(); var yl = new List<double>();
        for (var x = p_x1; x <= p_x2; x += p_s)
        {
            double? f = null;
            try { f = p_fn(x); }
            finally
            {
                if (f.HasValue) { xl.Add(x); yl.Add(f.Value); }
            }
        }
        return new { Xs = xl.ToArray(), Ys = yl.ToArray() };
    }

    var chrt = new Chart(); var ca = new ChartArea(); chrt.ChartAreas.Add(ca);
    ca.Area3DStyle.Enable3D = enable3D;
    ca.AxisX.Minimum = x1; ca.AxisX.Maximum = x2;   
    ca.AxisY.Minimum = ymin.Value; ca.AxisY.Maximum = ymax.Value;
        
    var sr = new Series(); chrt.Series.Add(sr);
    sr.ChartType = SeriesChartType.Spline; sr.Color = Color.Red;
    sr.MarkerColor = Color.Blue; sr.MarkerStyle = MarkerStyle.Circle;
    sr.MarkerSize = 2;
                
    var data = fArrPair(x1, x2, s, fn); sr.Points.DataBindXY(data.Xs, data.Ys); 
    var bm = new Bitmap(width: chrt.Width, height: chrt.Height);
    chrt.DrawToBitmap(bm, chrt.Bounds); return bm;
}

它使用 LinqPad 的功能在结果面板中显示 Windows 表单。
示例
添加引用(按F4
System.Drawing.dllSystem.Windows.Forms.dll >, System.Windows.Forms.DataVisualization.dll
并添加这些程序集中的所有命名空间。


其他提示/进一步阅读:

  • 想要在 Visual Studio 中使用 LinqPad?这是如何做到这一点

  • 需要将 LinqPad 作为“便携式应用程序”阅读此处如何执行此操作。

  • Joe 的 LinqPad 网站是一个很好的来源。在 LinqPad 内部,Help ->新功能为您提供有关新功能和方法的提示。 LinqPad 论坛 还包含有用的提示。

  • 也有帮助:这个 关于 Linq(Pad) 调试的文章。

  • 使用 lprun.exe批处理脚本中运行 LINQ 查询阅读本文 了解更多详细信息。
    例如:
    echo Customers.Take(100) >脚本.txt
    lprun -lang=e -cxname=CompanyServer.CustomerDb script.txt
    在此示例中,查询是一个简单的 LINQ 表达式。当然,您也可以使用-lang=program来准备复杂的查询来激活程序模式。

  • 您可以在 LinqPad 左侧的我的查询选项卡中编写和存储扩展方法:树的最后一项名为我的扩展;双击它打开一个文件,您可以在其中编写可用于所有查询的扩展名。将它们放入公共静态类 MyExtensions 中,并使用 Main() 方法包含对扩展的测试。


此处继续...

LinqPad extension methods  -  Part 1 of 2

Go to part 2


Besides the well-known myQuery.Dump("Query result:"), another feature to mention is the Util class: It contains many quite handy methods (some of them I've mentioned, but there are a lot more).

Also interesting is that you can modify the way Dump() works.

Finally I'll show you how you can make changes permanent (i.e. insert, update, delete LINQ queries) using SubmitChanges() or SaveChanges() as well as how you can access the internal connection object of LinqPad.

To round it up, I'll show you how you can create simple 2d graphic inside of LinqPad (drawing lines, bitmaps or functions).

So, here's a collection of built in LinqPad features (from my own experience with the tool):


.Dump()

(parameters available in LinqPad v5.03.08 and above)

The .Dump() extension method consumes and prints (almost) everything.

But did you know there are a couple of parameters available?
Take a look at this code snippet:

var obj=new { a="Hello", b=5, c="World", d=new { y=5, z=10 } };
obj.Dump(description: "1st example", depth: 5, toDataGrid: false, exclude: "b,d");
obj.Dump("2nd example", exclude: "a,c");
obj.Dump("2nd example", exclude: "+b,d"); // new in V5.06.06 beta

The 1st example prints only variables a and c and hides b and d, the 2nd example does the opposite (note that it specifies only 2 of the available parameters). The variablesy and z cannot be hidden individually, because they are not at the top level.

The following parameters are available (all are optional):

  • description [string] - provides a description for the object to dump
  • depth [int?] - limits how deep the objects are recursively inspected
  • toDataGrid [bool] - if true, the output is formatted as a datagrid rather than as RichText
  • exclude [string] - if you provide a comma-separated list of variables, they will be excluded from the output (in the example "a,c": b and d are shown, a and c are hidden)
  • exclude [string] with "+" prefix - the prefix inverts the logic of the exclude parameter. This means, if you provide a comma-separated list of variables, all except the ones specified are hidden (in the example "+b,d": b and d are shown, all others hidden)
  • store included and excluded properties in a variable (new since LinqPad V5.09.04):
    var x=Util.ToExpando(obj, "a, c", "b, d"); x.Dump();
    The first string contains a list of properties to include, the second string a list to exclude
  • expand on click: If you use .OnDemand("click me").Dump(); instead of .Dump(), it will display a link you can click on to expand. Useful if you want to inspect values, e.g. Util.OnDemand("Customer-ID: " + customerObject.ID.ToString(), ()=>customerObject, false).Dump(); to always show the ID per default but reveal the details of customerObject only if you're interested in.

More advanced topics about Dump can be found
here (Customizing Dump) and there (Extensions Part 2).


Util.WriteCsv

(new: available since LinqPad version v4.45.05 (beta))

Util.WriteCsv (Customers, @"c:\temp\customers.csv");

This will write the content of the table Customers to the CSV file c:\temp\customers.csv. You can also find a nice example how to use Util.WriteCsv and then display the CSV data in Linqpad's result window here.

Hints:

  • To get/create a CSV file which is in the same directory as the query, you can use:
    var csvFile=Util.CurrentQueryPath.Replace(".linq", ".csv");

  • If the table is large, use ObjectTrackingEnabled = false; before you write the CSV to avoid caching it in memory.

  • If you want to output a table in XML format rather than as comma-separated file, you can do it like:

      var xmlFile=Util.CurrentQueryPath.Replace(".linq", ".xml");
      var xml = XElement.Load(xmlFile);
      var query =
        from e in xml.Elements()
        where e.Attribute("attr1").Value == "a"
        select e;
      query.Dump();
    

This example returns all elements having the attribute attr1 which contains the value "a" from an XML file which has the same name as the query and is contained in the same path. Check out this link for more code samples.


Util.GetPassword

var pwd = Util.GetPassword("UserXY");

This will retrieve the password from LinqPad's built in password manager. To create & change the password, open the "Password manager" menu item in the "File" menu of LinqPad. If there is no password saved when you run the C# code, a password dialog will open up asking you for the password and you have the choice to create and save it on the fly by checking the save password checkbox (in the example, the password for "UserXY" would be saved, and later on you can find this entry in the Password manager).

Advantages are that you can store the password in the LinqScripts you create securely, separately and encrypted in Windows' user profile (it is stored in %localappdata%\LINQPad\Passwords as a file). LinqPad uses Windows DPAPI to protect the password.

Also, the password is stored centrally, so if you need to change it, you can do it in the menu and it immediately applies to all scripts you've created.

Notes:

  • If you don't want to save the password and just bring up a password dialog, you can use the 2nd parameter as follows:
    var pwd = Util.GetPassword("UserXY", true);
    This will uncheck the save password checkbox in the password dialog (however, the user is still able to check it and choose to save anyway).

  • If you require the password to be stored in a SecureString, you can use this helper function (n.b.: to get the extension method .ToSecureString() used, please follow this link at Stackoverflow - it also allows you to convert it back if needed):
    System.Security.SecureString GetPasswordSecure(string Name, bool noDefaultSave=true)
    {
      return Util.GetPassword(Name, noDefaultSave).ToSecureString();
    }


Util.Cmd

This method works like a command processor. You can invoke all commands you know from the Windows console.

Example 1 - dir:

Util.Cmd(@"dir C:\");

This will output the result of the directory without the need to .Dump it. Storing it in a variable has the advantage that you can use further Linq queries on it. For example:

var path=@"C:\windows\system32"; 
var dirSwitch="/s/b";
var x=Util.Cmd(String.Format(@"dir ""{0}"" {1}", path, dirSwitch), true);
var q=from d in x 
        where d.Contains(".exe") || d.Contains(".dll")              
        orderby d
    select d;
q.Dump();

This will dump all files with file extensions ".exe" or ".dll" contained in C:\windows\system32. The /s switch is used to recurse all subdirectories and /b is used for bare output format. Note that the second parameter of the Cmd method is specified to suppress the console output in order to show only the filtered result using the Dump method.

You can see that this is more flexible than the wildcards you have with dir since you can use the full flexibility of Linq's query engine.

Example 2 - text editor:

You can open a file in Notepad like this:

var filePath=@"C:\HelloWorld.txt";
Util.Cmd(@"%systemroot%\system32\notepad.exe", filePath);

Util.Image

Displays images from an URL. Example:

var url = "http://chart.apis.google.com/chart?cht=p3&chd=s:Uf9a&chs=350x140&chl=January|February|March|April";
Util.Image(url).Dump();

Util.ProgressBar, Util.Progress

Using Util.ProgressBar allows you to display a progress bar. You can use the following helper class:

public class ProgressBar
{
    Util.ProgressBar prog;
    
    public ProgressBar() 
    { 
        Init("Processing"); 
    }
    
    private void Init(string msg)
    {
        prog = new Util.ProgressBar (msg).Dump();
        prog.Percent=0;
    }

    public void Update(int percent)
    {
        Update(percent, null);
    }   
    
    public void Update(int percent, string msg)
    {
        prog.Percent=percent;
        if (String.IsNullOrEmpty(msg))
        {
            if (percent>99) prog.Caption="Done.";
        }
        else
        {
            prog.Caption=msg;
        }
    }
}

Simply use it as the following example shows:

void Main()
{
    var pb1= new ProgressBar();
    Thread.Sleep(50);
    pb1.Update(50, "Doing something"); Thread.Sleep(550);
    pb1.Update(100); Thread.Sleep(50);
}

You can alternatively use Util.Progress to update LinqPads integrated progress bar, for example:

Util.Progress = 25; // 25 percent complete

The difference is that it will not display in the results window, and you can't assign a message to it.


Util.RawHtml

Displays HTML in the output window. Example:

Util.RawHtml (new XElement ("h1", "This is a big heading")).Dump();

Hyperlinq, Util.HorizontalRun

You can use this example function

public void ShowUrl(string strURL, string Title)
{
    Action showURL = delegate() { Process.Start("iexplore.exe", strURL); };
    var url = new Hyperlinq(showURL, "this link", true);
    Util.HorizontalRun (true, "Click ", url, " for details.").Dump(Title);
}

to show hyperlinks in the result window - or any actions like opening your favourite editor.
Usage:

ShowUrl("http://stackoverflow.com", "Check out StackOverflow");

Note that this function always works, while new Hyperlinq ("http://myURL", "Web site").Dump(); does not work for some kind of URLs (especially, if you have to pass port names like ":1234" as part of the URL).


Util.ReadLine

Reads input from the console. Example:

int age = Util.ReadLine<int> ("Enter your age");

As a synonym for Util.ReadLine<string>(), you can use Console.ReadLine() as well.

But there is more! You can create a simple JSON parser with the following snippet - quite useful, for example if you want to parse and test a JSON string on the fly. Save the following snippet as JSONAnalyzer.linq using a text editor and then open it in LinqPad (this is to add the references easily on the fly):

<Query Kind="Program">
    <Reference><RuntimeDirectory>\System.Web.Extensions.dll</Reference>
    <Namespace>System.Web.Script.Serialization</Namespace>
</Query>

void Main()
{
    var jsonData=Util.ReadLine<string>("Enter JSON string:");
    var jsonAsObject = new JavaScriptSerializer().Deserialize<object>(jsonData);
    jsonAsObject.Dump("Deserialized JSON");
}

Now you can run it and simply paste a JSON string from the clipboard into the console - it will use the Dump function to display it as an object nicely - and you also get the error messages of the parser on the screen to fix issues. Very useful for debugging AJAX.

JSON


Util.ClearResults

If you need to clear the results window inside your script, use:

Util.ClearResults();

Either use it at the top of your script, or - if you're running multiple queries in a script - you should wait for user input before blanking the screen (e.g. by preceding it with Util.ReadLine).


Custom .Dump() - ICustomMemberProvider

Also interesting is, that you can change the output of the .Dump() method. Simply implement the interface ICustomMemberProvider, e.g.

public class test : ICustomMemberProvider 
{

      IEnumerable<string> ICustomMemberProvider.GetNames() {
        return new List<string>{"Hint", "constMember1", "constMember2", "myprop"};
      }
      
      IEnumerable<Type> ICustomMemberProvider.GetTypes() 
      {
        return new List<Type>{typeof(string), typeof(string[]), 
            typeof(string), typeof(string)};
      }
      
      IEnumerable<object> ICustomMemberProvider.GetValues() 
      {
        return new List<object>{
        "This class contains custom properties for .Dump()", 
        new string[]{"A", "B", "C"}, "blabla", abc};
      }

      public string abc = "Hello1"; // abc is shown as "myprop"
      public string xyz = "Hello2"; // xyz is entirely hidden
}

If you create an instance of this class, like

var obj1 = new test();
obj1.Dump("Test");

then it will output only Hint, constMember1, constMember2, and myprop, but not property xyz:

Linqpad dump


Displaying a MessageBox or InputBox in LinqPad

If you need to display a messagebox, look here how to do it.

For example, you can display an InputBox by using the following code

void Main()
{
    string inputValue="John Doe"; 
    inputValue=Interaction.InputBox("Enter user name", "Query", inputValue);
    if (!string.IsNullOrEmpty(inputValue)) // not cancelled and value entered
    {
        inputValue.Dump("You have entered;"); // either display it in results window
        Interaction.MsgBox(inputValue, MsgBoxStyle.OkOnly, "Result"); // or as MsgBox
    }
}

(don't forget to press F4 and add Microsoft.VisualBasic.dll and its namespaces to make this work)


Util.Run

(new: available since LinqPad version v4.52.1 (beta))

Allows you to run another LINQPad script from within your script or within your own .NET program or Windows service (by referencing the LINQPad4-AnyCPU version of LINQPad.exe). It executes the script just as the command line tool lprun.exe would do it.

Examples:

const string path=@"C:\myScripts\LinqPad\";
var dummy=new LINQPad.QueryResultFormat(); // needed to call Util.Run
Util.Run(path+"foo.linq", dummy);

This example runs the script foo.linq, which contains the following sample code:

void Main(string[] args)
{
    #if CMD
       "I'm been called from lprun! (command line)".Dump();
    #else
       "I'm running in the LINQPad GUI!".Dump();
       args = new[] { "testhost", "[email protected]", "[email protected]", "Test Subject" };
    #endif
    args.Dump("Args");
}

It allows you to check if the script was run from inside the LinqPad GUI or via lprun.exe or with Util.Run.

Note: The following variants of invocation might be helpful:

Util.Run(path+"foo.linq", dummy).Dump(); // obviously dumps the script output!
Util.Run(path+"foo.linq", dummy).Save(path+"foo.log"); // writes output into log
Util.Run(path+"foo.linq", dummy).SaveAsync(path+"foo1.log");     // async output log

SubmitChanges() - Linq To SQL

If you're using LinqToSQL, you might want to make changes permanent (for insert/update/delete operations).
Since the database context is implicitly made by LinqPad, you need to call SubmitChanges() after each change as shown below.

Examples for (LinqPad-)Northwind database:

Insert

var newP = new Products() { ProductID=pID, CategoryID=cID, 
            ProductName="Salmon#"+pID.ToString() };
Products.InsertOnSubmit(newP);
SubmitChanges();    

Update

var prod=(from p in Products
            where p.ProductName.Contains("Salmon")
            select p).FirstOrDefault();
prod.ProductName="Trout#"+prod.ProductID.ToString();
SubmitChanges(); 

Delete

var itemsToDelete=Products.Where(p=> p.ProductName.Contains("Salmon") ||
    p.ProductName.Contains("Trout"));
foreach(var item in itemsToDelete) { Products.DeleteOnSubmit(item); }
SubmitChanges();

Note: In order to get valid IDs for the previous examples, you can use:

var cID = (from c in Categories 
            where c.CategoryName.Contains("Seafood") 
            select c).FirstOrDefault().CategoryID;

var pID = Products.Count()+1;

before you invoke them.


SaveChanges() - Entity Framework

If you're using Entity Framework, you might want to make changes permanent as well (for insert/update/delete operations).
Since the database context is implicitly made by LinqPad, you need to call SaveChanges() after each change as shown below.

The examples are basically the same as before for LinqToSQL, but you need to use SaveChanges() instead, and for inserting and deleting the methods have changed as well.

Insert

var newP = new Products() { ProductID=pID, CategoryID=cID, 
            ProductName="Salmon#"+pID.ToString() };
Products.Add(newP);
SaveChanges();  

Update

var prod=(from p in Products
            where p.ProductName.Contains("Salmon")
            select p).FirstOrDefault();
prod.ProductName="Trout#"+prod.ProductID.ToString();
SaveChanges(); 

Delete

var itemsToDelete=Products.Where(p=> p.ProductName.Contains("Salmon") ||
    p.ProductName.Contains("Trout"));
foreach(var item in itemsToDelete) { Products.Remove(item); }
SaveChanges();

Note: In order to get valid IDs for the previous examples, you can use:

var cID = (from c in Categories 
            where c.CategoryName.Contains("Seafood") 
            select c).FirstOrDefault().CategoryID;

var pID = Products.Count()+1;

before you invoke them.

If you need to have transactions, take a look at this post: How to nest transactions.


this - database context

In LinqPad, the database context is applied automatically by using the combobox at the top and picking the right database for your query.
But sometimes, it is useful to reference it explicitly, for example if you copy some code from your project out of Visual Studio, and paste it into LinqPad.

Your code snippet taken from the Visual Studio project very likely looks like this:

var prod=(from p in dc.Products
            where p.ProductName.Contains("Salmon")
            select p).FirstOrDefault();
prod.ProductName="Trout#"+prod.ProductID.ToString();
dc.SaveChanges(); 

Now what to do with dc? Of course, you could remove each occurrence of dc. in your query, but it is much easier.
Just add it to the top of your snippet like so:

UserQuery dc { get => this; }
void Main()
{
    var prod=(from p in dc.Products
                where p.ProductName.Contains("Salmon")
                select p).FirstOrDefault();
    prod.ProductName="Trout#"+prod.ProductID.ToString();
    dc.SaveChanges(); 
}   

and the code will work instantly!


this.Connection

Using LinqPad with OleDb, converting a datatable to Linq object, SQL queries in Linq

The following code snippet helps you to use LinqPad with OleDb. Add System.Data.OleDb from the System.Data assembly to the query properties, then paste the following code into Main():

var connStr="Provider=SQLOLEDB.1;"+this.Connection.ConnectionString; 

OleDbConnection conn = new OleDbConnection(connStr);
DataSet myDS = new DataSet();
conn.Open();

string sql = @"SELECT * from Customers";
OleDbDataAdapter adpt = new OleDbDataAdapter();
adpt.SelectCommand = new OleDbCommand(sql, conn); 
adpt.Fill(myDS);

myDS.Dump();

Now add a SqlServer connection to LinqPad and add the Northwind database in order to run this example.

N.B.: If you just want to get the database and server of the currently selected connection, you can use this code snippet:

void Main()
{
    var dc=this;
    var tgtSrv=dc.Connection.DataSource;
    var tgtDb=dc.Connection.ConnectionString.Split(';').Select(s=>s.Trim())
        .Where(x=>x.StartsWith("initial catalog", StringComparison.InvariantCultureIgnoreCase))
        .ToArray()[0].Split('=')[1];
    tgtSrv.Dump();
    tgtDb.Dump();
}

You can even convert myDS into Linq, the answers to the following question show how to do it: Nice examples of using .NET 4 dynamic keyword with Linq

One more example: Suppose your DBA gives you a SQL query and you want to analyze the results in LinqPad - of course in Linq, not in SQL. You can do the following:

void Main()
{
    var dc=this;
    
    // do the SQL query
    var cmd =
        "SELECT Orders.OrderID, Orders.CustomerID, Customers.CompanyName,"
        +"       Customers.Address, Customers.City"
        +" FROM Customers INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID";
    var results = dc.ExecuteQuery<OrderResult>(cmd);
    
    // just get the cities back, ordered ascending
    results.Select(x=>x.City).Distinct().OrderBy(x=>x).Dump();
}

class OrderResult
{   // put here all the fields you're returning from the SELECT
    public dynamic OrderID=null; 
    public dynamic CustomerID=null;
    public dynamic CompanyName=null;
    public dynamic Address=null;
    public dynamic City=null;
}

In this example the DBA's SELECT query is just "thrown into" the command text, and the results are filtered and ordered by City.
Of course, this is a simplified example, your DBA would probably give you a more complex script, but you're getting the idea: Add a supporting result class which contains all the fields from the SELECT clause, then you can directly use it.
You can even take the result from a stored procedure this way and use it in Linq. As you can see, in this example I don't care about the data type and use dynamic to express it.
So this is really about rapid programming to be able to analyze data quickly. You shouldn't do this in your real application for various reasons (SQL injection, because you can use EF from the beginning etc).


PanelManager

Draw graphic in LinqPad, part 1

To use the examples below, press F4 and add System.Windows.dll, System.Windows.Forms.dll, WindowsFormsIntegration.dll, PresentationCore.dll and PresentationFramework.dll to your LinqPad program and also add the namespace System.Windows.Shapes.

The 1st example simply draws a line:

var myLine = new Line();
myLine.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
myLine.X1 = 1; myLine.X2 = 50;
myLine.Y1 = 1; myLine.Y2 = 50;
myLine.StrokeThickness = 2;
PanelManager.DisplayWpfElement(myLine, "Graphic");

The 2nd example shows how you can display graphic in LinqPad by using the PanelManager. Normally LinqPad only supports Wpf objects. This example uses System.Windows.Forms.Integration.WindowsFormsHost to make a PictureBox available (it was inspired by this):

// needs (F4): System.Windows.dll, System.Windows.Forms.dll, 
// WindowsFormsIntegration.dll, PresentationCore.dll, PresentationFramework.dll 
void Main()
{       
    var wfHost1 = new System.Windows.Forms.Integration.WindowsFormsHost();
    wfHost1.Height=175; wfHost1.Width=175; wfHost1.Name="Picturebox1";
    wfHost1.HorizontalAlignment=System.Windows.HorizontalAlignment.Left;
    wfHost1.VerticalAlignment=System.Windows.VerticalAlignment.Top;
    System.Windows.Forms.PictureBox pBox1 = new System.Windows.Forms.PictureBox();
    wfHost1.Child = pBox1;
    pBox1.Paint += new System.Windows.Forms.PaintEventHandler(picturebox1_Paint);
    PanelManager.StackWpfElement(wfHost1, "Picture");
} 

public string pathImg
{
    get { return System.IO.Path.Combine(@"C:\Users\Public\Pictures\Sample Pictures\", 
            "Tulips.jpg"); } 
}

// Define other methods and classes here
public void picturebox1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
    // https://stackoverflow.com/a/14143574/1016343
    System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(pathImg);
    System.Drawing.Point ulPoint = new System.Drawing.Point(0, 0);
    e.Graphics.DrawImage(bmp, ulPoint.X, ulPoint.Y, 175, 175);
}

This will create the following graphic (panel items "Graphic" and "Picture" are added by the examples above):

Showing_Graphic_in_LinqPad

If you want to display the images from the Northwind database, you can do the following:
Change the image file name to "NorthwindPics.jpg", then add the following code at the beginning of the 2nd example's Main() method:

var img = (from e in this.Employees select e).FirstOrDefault().Photo.ToArray();
using (FileStream fs1 = new FileStream(pathImg, FileMode.Create))
{
    const int offset=78;
    fs1.Write(img, offset, img.Length-offset);
    fs1.Close();
}

It will read the first record from the Employees table and display the picture.

Check out the following links to find out more:
Shapes and basic drawing in WPF
LinqPad custom visualizers

Note: You can achieve the same without the PanelManager as well, as the following example, which I saw here shows:

// using System.Drawing;
using (var image=new Bitmap(100, 100))
using (var gr = Graphics.FromImage(image))
{
    gr.FillRectangle(Brushes.Gold, 0, 0, 100, 100);
    gr.DrawEllipse(Pens.Blue, 5, 5, 90, 90);
    gr.Save();
    image.Dump();
}

It is using the .Dump() command to display it. You can invoke image.Dump() multiple times and it will append the image.


Windows Forms

Draw graphic in LinqPad, part 2

The following example, inspired by this post, is showing how to implement a function plotter in Linqpad using C#7:

void Main()
{
    fnPlotter(x1: -1, x2: 1, fn: (double x) => Math.Pow(x, 3)).Dump();
}

public static Bitmap fnPlotter(double x1=-3, double x2=3, double s=0.05, 
                                   double? ymin=null, double? ymax=null, 
                                   Func<double, double> fn = null, bool enable3D=true)
{
    ymin = ymin ?? x1; ymax = ymax ?? x2;
    
    dynamic fArrPair(double p_x1 = -3, double p_x2 = 3, double p_s = 0.01, 
                          Func<double, double> p_fn = null)
    {
        if (p_fn == null) p_fn = ((xf) => { return xf; }); // identity as default
        var xl = new List<double>(); var yl = new List<double>();
        for (var x = p_x1; x <= p_x2; x += p_s)
        {
            double? f = null;
            try { f = p_fn(x); }
            finally
            {
                if (f.HasValue) { xl.Add(x); yl.Add(f.Value); }
            }
        }
        return new { Xs = xl.ToArray(), Ys = yl.ToArray() };
    }

    var chrt = new Chart(); var ca = new ChartArea(); chrt.ChartAreas.Add(ca);
    ca.Area3DStyle.Enable3D = enable3D;
    ca.AxisX.Minimum = x1; ca.AxisX.Maximum = x2;   
    ca.AxisY.Minimum = ymin.Value; ca.AxisY.Maximum = ymax.Value;
        
    var sr = new Series(); chrt.Series.Add(sr);
    sr.ChartType = SeriesChartType.Spline; sr.Color = Color.Red;
    sr.MarkerColor = Color.Blue; sr.MarkerStyle = MarkerStyle.Circle;
    sr.MarkerSize = 2;
                
    var data = fArrPair(x1, x2, s, fn); sr.Points.DataBindXY(data.Xs, data.Ys); 
    var bm = new Bitmap(width: chrt.Width, height: chrt.Height);
    chrt.DrawToBitmap(bm, chrt.Bounds); return bm;
}

It is using the capability of LinqPad to display Windows forms in the results panel.
Example
Add references (press F4):
System.Drawing.dll, System.Windows.Forms.dll, System.Windows.Forms.DataVisualization.dll
and add all namespaces from these assemblies.


Additional hints / further reading:

  • Want to use LinqPad in Visual Studio? Here's how you can do that.

  • Need to have LinqPad as a "Portable app"? Read here how to do that.

  • Joe's website for LinqPad is an excellent source. Inside LinqPad, Help -> What's New gives you hints about new functions and methods. The LinqPad Forum also contains helpful hints.

  • Also helpful: This article about Linq(Pad) debugging.

  • Use lprun.exe for running LINQ queries in your batch scripts. Read this article for more details.
    For example:
    echo Customers.Take(100) > script.txt
    lprun -lang=e -cxname=CompanyServer.CustomerDb script.txt
    In this example, the query is a simple LINQ expression. Of course, you can prepare complex queries as well using -lang=program to activate the program mode.

  • You can write and store extension methods in the My Queries tab on the left hand side of LinqPad: The last item of the tree is named My Extensions; double click on it to open a file where you can write extensions that are available to all your queries. Put them into the public static class MyExtensions, and use the Main() method to include tests for your extensions.


Continued here...

空‖城人不在 2024-09-22 09:21:06

LinqPad 扩展方法  - 第 2 部分(共 2 部分)

转到第 1 部分


已达到StackOverflow 文本限制 我的之前的答案中有 30000 个字符,但 LinqPad 中还有更酷的扩展。我想提及其中一些:


自动滚动和其他快捷方式

  • 在查询运行时自动滚动结果窗口的末尾
    (使用 .Dump() 语句):
    Shift+Control+E< /strong> 切换(打开或关闭)


  • 多个入口点:< br>按 Alt+Shift+1 运行 Main1(), < strong>Alt+Shift+2 运行 Main2(),依此类推。
    注意,您仍然需要 void Main() 作为主入口点,上述方法是附加(可选)入口点。

  • 运行 xUnit 测试:
    Alt+Shift+T 运行全部用 [Fact][Theory] 装饰的 xUnit 测试(作为准备,您需要通过 Query -> 添加 xUnit 支持)添加 xUnit 测试支持菜单)


环境

这不是一个 LinqPad 扩展,而是一个 .NET 类,但由于它很有用,所以无论如何我都会提到它。
您可以获得许多可以在脚本中使用的有用信息,例如:

Environment.UserDomainName.Dump();
Environment.MachineName.Dump();
Environment.UserName.Dump();
Environment.CurrentDirectory.Dump();
Environment.SystemDirectory.Dump();

NB 为了获取 Domain\UserName 我将使用 System.Security.Principal.WindowsIdentity .GetCurrent().Name
而不是 Environment.UserDomainName+@"\"+Environment.UserName


中编写您自己的扩展

在 LinqPad ListTables

您知道您可以在 LinqPad 中编写您自己的扩展,并在所有查询中可用吗?
您可以这样做:
在 LinqPad 中,转到左侧的“我的查询”选项卡,向下滚动到末尾,直到看到“我的扩展”。双击它,它将打开一个名为“我的扩展”的特殊查询窗口。您在那里编写的内容将在所有查询中可用。

现在将以下代码粘贴到其中,然后使用 Ctrl+S 保存:

我的扩展

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(LinqPad 的作者)向我提供了这个代码片段 - 它显示了如何将数据上下文传递到我的扩展。
注意:对于 LinqPad 6 或更高版本,您需要按 F4 获取查询属性,并勾选“引用 LINQ-to-SQL 程序集”才能使其正常工作。

按以下方式使用此扩展:在 LinqPad 中打开一个新的 C# 查询窗口(使用 Ctrl+N),然后连接到您选择的数据库,并键入:

新查询

void Main()
{
    this.ListTables();
}

重要:如果您未连接到数据库,则扩展不可用,LinqPad 将显示错误。因此,首先连接到数据库,然后输入 this.ListTables();

请注意,IntelliSense 将显示我们在“我的扩展”中键入的 XML 注释的摘要。运行它后,您将获得当前数据库的表列表。


LinqPad 中的配置文件 (appsettings.json)


之前我已经展示了如何使用 MyExtensions。现在,如果您想要一个全局 appsettings.json 文件或每个脚本一个,您可以使用以下扩展:

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;
    }
}

您还可以将其直接存储在 C# 程序中,但这种方式默认可用,您只需执行 NUGET 加载一次。

假设您编写了一个 LinqPad 程序“YourCSharpProgram.linq”
现在您可以提供类似

var config1 = MyExtensions.AppSettings();

或类似的

var config2 = MyExtensions.AppSettings("C:\MyGlobalSettings\appsettings.json");

配置第一个选项 config1 将需要文件 "YourCSharpProgram.linq" 下的设置,并将 "appsettings.json" 附加到它,这意味着您的设置必须位于与程序相同的文件夹中的 "YourCSharpProgram.linq.appsettings.json" 中。
第二个选项仅使用指定的绝对路径。

如果您的设置文件包含,

{
    "AzureStorage": {
        "StorageConnectionString": "some connection string"
    }
}

您可以像注意:一样访问它。

var config = MyExtensions.AppSettings();
string connectionString = config.GetSection("AzureStorage").GetSection("StorageConnectionString").Value.ToString();
connectionString.Dump();

使用配置的第二种方法是将 JSON 文件的绝对路径放在 LinqPads F4 对话框中。在 LinqPad 5 中,情况更好,因为设置文件有一个单独的选项卡(其中有 AppConfig,因为版本 5 适用于 .NET,而不适用于 .NET core)。您必须像引用程序集一样引用它,而且这并不明显。所以我更喜欢上面描述的那样。


.Dump() - 重新审视

单击操作扩展

创建一个带有出现在 LinqPad 的“结果”选项卡上的单击操作的按钮很简单:

using LINQPad.Controls;
void Main()
{
    var btn = (new Button("Click me", (Button b) =>
    {
        // display message on click, then hide button
        "Button was clicked".Dump();
        b.Visible = false;
    })).Dump();
}

但有时,如果您可以单击一个按钮来对特定行执行操作,那么它会很有用 - 换句话说,产生如下输出:

点击列的示例

您可以通过编写一个扩展来完成此操作(您可以将其存储在“我的扩展”中,如前所述 - 但您也可以在您的应用程序中拥有一个单独的静态扩展类LinqPad 程序)。

扩展类的骨架:

public static class Extensions
{   
    // add here: ActionButton<TSource>
        
    // add here: DumpAction<T>

    // optional: DumpActionDict<K, V>
}

下面的所有静态方法都需要位于此静态非嵌套扩展类内部(或者在 LinqPad 的“我的扩展”中,如前所述)。

其余代码直接进入 void Main() { ... } 方法。 注意项目类型必须是C# 程序

首先,我们需要有一个操作按钮;为了以简单的方式创建一个,我们使用了这个辅助方法:

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); });
    

该函数采用源类型,并允许创建一个标有文本和选择器的按钮(我们很快就会看到它是什么)。

这样,我们就可以定义一个 DumpAction 方法,如下所示:

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);
}

这会附加一个列,其中包含可以单击的按钮。
然后我们需要一个 DumpClick 方法,

public static void DumpClick<T>(this IEnumerable<T> data, string description="", string text = "")

{
    if (!string.IsNullOrEmpty(description)) text.Dump(description);
    foreach (var element in data)
    {
        Util.OnDemand(element.ToString(), () => myFunc(element) ).Dump();
    }
    if (!string.IsNullOrEmpty(description)) "--------".Dump();
}

您可以按如下方式使用它:

// list to display
var myList = new List<string>()
{
    "Apple", "Pie"
};

您所要做的就是:

myList.DumpAction((x) => myAction(x), "Click Extension");

其作用与您从 .Dump() 方法中了解到的基本相同。
现在所需要做的就是定义一个正在调用的方法 myAction

void myAction(string payload)
{
    payload.Dump("myAction");
}

如果您单击将列表项的字符串值传递给它的按钮,则会调用该方法。

注意:您可以对字典执行相同的操作,上面的扩展已经适用于它们,但它占用了大量空间
在每行的结果窗口中 - 要解决这个问题,您可以定义第二个扩展方法:

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);
}

使用它如下:

var myDict = new Dictionary<string, string>()
{
    ["a"] = "x",
    ["b"] = "y"
};

myDict.DumpActionDict((x) => myAction(x), "Extension for dict");

注意: 也可以以不同的方式设置按钮内文本的格式,请看一下这个例子:

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;
}

这将使按钮文本变小。您可以使用任何 HTML 元素。


使用 Util.HighlightIf 对转储进行着色

您可以使用 Util.HighlightIf(condition, object)Util.HighlightIf(condition, htmlcolor, object) 创建彩色转储。

以下示例取自 LinqPad 的发行说明,并对其进行了更多着色,以显示如何进行:

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", 
            new {file.Name, 
              Length=Util.HighlightIf(file.Length>999999,"orange",file.Length),
              LastWriteDate=DateTime.Today.Date.ToString("yyyy-MM-dd")}
        )))))).Dump();
}

现在,它有什么作用?
它根据

  • 文件扩展名为单元格着色。文件扩展名 .bat.txt.json.cmd.dll、.xml.exe 各自具有不同的颜色(有些颜色相同)。
  • 文件大小。如果大小超过 999999 字节,其单元格将显示为橙色。

这将创建一个转储,如下所示:

colordump


内联更新消息

有时,覆盖您转储的文本比将其放入新行中更有用,例如,如果您正在执行长操作- 运行查询并希望显示其进度等(另请参见下面的进度栏)。这可以通过使用 DumpContainer 来完成,您可以按照

示例 1

void Main()
{
   var dc = new DumpContainer("Doing something ... ").Dump("Some Action");
   System.Threading.Thread.Sleep(3000); // wait 3 seconds
   dc.Content += "Done.";
}

中所示的方式使用它DumpContainerAnimation

注意更多内容复杂的对象,您可能必须使用 dc.UpdateContent(obj); 而不是 dc.Content=...

示例 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!");
}

显示进度 - Util.ProgressBar

显示进度也可以通过使用 ProgressBar 来完成,如下所示:

示例:

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";
}

这与之前的转储示例类似,但是这个时间显示一个漂亮的进度条动画。

输入图像描述这里


JavaScript 函数(使用 .Dump()

从 LinqPad 5.42 beta 版本开始,您可以嵌入 JavaScript 函数并直接从 C# 代码调用它们。尽管这有一些限制(与 JSFiddle 相比),但它是在 LinqPad 中快速测试某些 JavaScript 代码的好方法。

示例:

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");
}

在此示例中,准备了一个带有一个参数的函数 jsFoo 并将其存储在变量 literal 中。然后,通过 .Dump().HtmlElement.InvokeScript(...) 呈现并调用它,并传递参数 testparam

JavaScript 函数使用 external.Log(...) 在 LinqPad 的输出窗口中输出文本,并使用 alert(...) 显示弹出消息。

您可以通过添加以下扩展类/方法来简化此操作:

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);
    }
}

然后您可以按如下方式调用前面的示例:

    // 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");

这具有相同的效果,但更易于阅读(如果您打算执行更多 JavaScript ; -) )。

另一种选择是,如果您喜欢 Lambda 表达式,并且不喜欢每次调用时都将函数名称指定为字符串,则可以这样做:

var jsFoo = ScriptExtension.CreateJavaScript(
            @"function jsFoo(x) { ...  }"); 
ScriptExtension.RunJavaScript(() => jsFoo, "testparam");

只要您已将辅助函数添加

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);
}

到类 ScriptExtension.这将解析您使用的变量名称(此处为 jsFoo),该名称恰好与 JavaScript 函数本身同名(请注意如何使用 lambda 表达式来解析变量名称,这不能通过在函数内部使用 nameof(paramName))。


xUnit - 使用 LinqPad 进行单元测试

您知道可以在 LinqPad 中编写单元测试吗?例如,您可以使用 xUnit 框架。对于 LinqPad 版本 5,可以通过 LinqPad 的 NUGET 支持获得 - 通过 F4 - 在对话框中单击 添加 NUGET...。从 LinqPad 版本 6 开始,它是内置的(菜单查询 -> 添加 XUnit 测试支持)。以下是如何操作的分步说明
将 xUnit 与 LinqPad V5、V6 或 V7 结合使用。

示例 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");   
}

此示例需要 Northwind 示例数据库(设置作为 Linq to SQL 或作为 Entity Framework Core Connection),分配给查询,并添加 XUnit 测试支持(选择查询 -> 在 LinqPad 中添加 XUnit 测试支持)。它将把所有类别和产品打印到结果窗口。如果数据库中没有类别或产品,则测试失败。

示例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);

此示例使用[Theory]和成员函数GetNumbers()来填充参数a、b、 cexpectedSum 为您提供比 [Fact] 更大的灵活性。 [Fact] 不能有任何参数。如果预期总和不等于 a+b+c 的总和,则测试失败。第二行就是这种情况:断言将输出:(a: 7, b: 0, c: 5, ExpectedSum: 13)

注意:您还可以多次使用InlineData(例如[InlineData(1,2,3,6)])来直接传递值的理论,甚至可以将其与 MemberData 属性结合起来。它将把所有值添加到测试中并执行它。


多数据库支持

LinqPad 的付费版本 (LinqPad 6 Premium) 支持查询中的多个数据库。

下面我将描述选择“Linq to SQL(针对 SQL Server 优化)”时获得数据库属性的步骤。

创建新连接或打开现有连接。打开数据库属性,选择一个数据库(不要使用“在 TreeView 中显示全部”)

数据库属性

然后勾选-标记“包括其他数据库” - 这将弹出另一个对话框,您可以在其中添加多个数据库:

其他数据库

单击从列表中选择...,您可以选择 + 选择另一个数据库。当您完成后,单击关闭关闭附加对话框,然后单击确定关闭数据库属性,

其中选择的数据库是“辅助”上下文(在“this”中列出了数据库名称) 。 “UserQuery),第一个数据库(您在“指定新的或现有的数据库”下选择的)是“主”上下文(这意味着表直接出现在“此”UserQuery 中)。

在连接窗口中,这将显示为

“.\MyInstance\AdventureWorks2017 + AdventureWorks2017 + Northwind”


在下面的代码中,我使用“AdventureWorks2017”作为主要上下文,使用“AdventureWorks2017”和“Northwind”作为次要上下文。

像这样准备,您可以执行以下操作:

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");
}

本示例中使用的两个示例数据库都来自 Microsoft,可以免费下载,并且它们都有一个 Products 表,但具有不同的属性/字段:您可以看到我已重命名 ProductName / Name,以便它在所有查询中显示为 Name。

该程序将为您提供结果:

结果

下载链接:
AdventureWorks、罗斯风, LinqPad


LinqPad extension methods  -  Part 2 of 2

Go to part 1


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:


Auto-Scrolling and other shortcuts

  • 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)


Environment

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.


Write your own extensions in LinqPad

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.


Configuration file (appsettings.json) in LinqPad


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.


.Dump() - revisited

Extension for click action

Creating a button with a click action appearing on the Results tab in LinqPad is easy:

using LINQPad.Controls;
void Main()
{
    var btn = (new Button("Click me", (Button b) =>
    {
        // display message on click, then hide button
        "Button was clicked".Dump();
        b.Visible = false;
    })).Dump();
}

But sometimes it is useful if you can click a button to perform an action on a particular row - in other words, something which produces an output like this:

Example for click column

You can do this by writing an Extension (which you could store in "My Extensions" as described earlier - but you can also have an individual static extension class inside your LinqPad program).

Skeleton of the extension class:

public static class Extensions
{   
    // add here: ActionButton<TSource>
        
    // add here: DumpAction<T>

    // optional: DumpActionDict<K, V>
}

All of the static methods below need to be inside of this static not nested extension class (or in LinqPad's "My Extensions" as mentioned before).

The rest of the code goes directly into the void Main() { ... } method. Note that the project type must be C# Program.

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.
Then we need a DumpClick method

public static void DumpClick<T>(this IEnumerable<T> data, string description="", string text = "")

{
    if (!string.IsNullOrEmpty(description)) text.Dump(description);
    foreach (var element in data)
    {
        Util.OnDemand(element.ToString(), () => myFunc(element) ).Dump();
    }
    if (!string.IsNullOrEmpty(description)) "--------".Dump();
}

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 = 
quot;<small>{text}</small>";
    return btn;
}

That will make the button text smaller. You can use any HTML element(s).


Coloring your dump with Util.HighlightIf

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", 
            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

  • file extension. The file extensions .bat,.txt, .json, .cmd, .dll, .xml and .exe have different colors for each of them (some share the same color).
  • file size. If the size exceeds 999999 bytes, its cell is colored in orange.

This will create a dump like:

colordump


Updating a message inline

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.";
}

DumpContainerAnimation

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(
quot;Countdown: {i}");
        System.Threading.Thread.Sleep(250);
    };
    dc.UpdateContent("Ready for take off!");
}

Showing progress - Util.ProgressBar

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.

enter image description here


JavaScript functions (using .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).


xUnit - Unit testing with LinqPad

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.


Multiple database support

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)

database properties

and then tick-mark "Include Additional Databases" - that will bring up another dialog where you can add multiple databases:

additional 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:

result

Download links:
AdventureWorks, Northwind, LinqPad


叫嚣ゝ 2024-09-22 09:21:06

Dump 是一个全局扩展方法,SubmitChanges 来自 DataContext 对象,该对象是一个 System.Data.Linq.DataContext 对象。

据我所知,LP 仅添加了转储和反汇编。不过我强烈建议在 Reflector 中打开它,看看还有什么可以使用的。更有趣的事情之一是 LINQPad.Util 命名空间,它有 LINQPad 内部使用的一些好东西。

Dump is a global extension method and SubmitChanges comes from the DataContext object which is a System.Data.Linq.DataContext object.

LP adds only Dump and Disassemble as far as I'm aware. Though I would highly recommend opening it in Reflector to see what else is there that can be used. One of the more interesting things is the LINQPad.Util namespace which has some goodies used by LINQPad internally.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文