Showing posts with label LinqPAD. Show all posts
Showing posts with label LinqPAD. Show all posts

August 25, 2026

Library And Test Projects Generator

Here is a Linqpad file. You specify the root directory name and it will generate a directory structure with a solution file in the root directory. Under the root directory are 2 project directories, one a c# library project and the other is an NUnit test project for testing the library. The root directory is created in the Temp directory.

async void Main()
{
    IFileSystem fileSystem = new FileSystem();

    string linqPadFile = "";
    string projectName = "YYY";
    var testDirPath = Path.Combine(Path.GetTempPath(), projectName);
    var rootDir = fileSystem.Directory.CreateDirectory(testDirPath);
    Debug.Assert(rootDir.Exists, $"Directory {rootDir.FullName} not found.");

    var projFileCreator = new CSharpProjectFileCreator(fileSystem);
    var response = projFileCreator.Create(new CreateProjectRequest(rootDir.CreateSubdirectory($"{projectName}"), $"{projectName}.csproj", 
        ["Microsoft.Extensions.Logging", "System.IO.Abstractions", "Microsoft.Extensions.DependencyInjection"], $"{projectName}.cs", internalsVisibleTo: $"{projectName}.NUnit"));
    var response2 = projFileCreator.Create(new CreateProjectRequest(rootDir.CreateSubdirectory($"{projectName}.NUnit"), $"{projectName}.NUnit.csproj", 
        ["NUnit", "NUnit3TestAdapter", "Microsoft.NET.Test.Sdk", "System.IO.Abstractions", "System.IO.Abstractions.TestingHelpers", "coverlet.collector"],
        $"{projectName}.NUnit.cs", NUnitSamples.NUnitSampleTest));
    projFileCreator.CreateSolutionFile(testDirPath, projectName);


    // Output
    //Console.WriteLine($"Project File Name: {projectFileName}");
    //Console.WriteLine("");
    //Console.WriteLine("--- Project Contents ---");
    //Console.WriteLine(projectFileContents);
}

public record CreateProjectRequest(IDirectoryInfo TargetDir, string ProjectFileName, string[] NugetReferences, string ContentFileName = "", string Content = "", string internalsVisibleTo = "");
public record CreateProjectResponse(bool Successful, string msg = "");


public class CSharpProjectFileCreator
{
    private readonly IFileSystem _fileSystem;

    public CSharpProjectFileCreator(IFileSystem fileSystem)
    {
        _fileSystem = fileSystem;
    }

    public CreateProjectResponse Create(CreateProjectRequest request)
    {
        bool fullySuccessful = false;
        if (!request.TargetDir.Exists)
        {
            return new CreateProjectResponse(false, $"Directory {request.TargetDir.FullName} not found.");
        }

        var projectFilePath = Path.Combine(request.TargetDir.FullName, request.ProjectFileName);
        var projectFi = _fileSystem.FileInfo.New(projectFilePath);
        var successful = GenerateProjectFile(projectFi.FullName, request.NugetReferences, request.internalsVisibleTo);
        if (!successful)
        {
            return new CreateProjectResponse(false, $"Project file {projectFi.FullName} not created.");
        }

        var contentFilePath = Path.Combine(request.TargetDir.FullName, request.ContentFileName);
        var contentFi = _fileSystem.FileInfo.New(contentFilePath);
        successful = CreateFile(contentFi.FullName, request.Content);

        if (!successful)
        {
            return new CreateProjectResponse(false, $"Project file {projectFi.FullName} created, but not the content file {contentFi.FullName} .");
        }

        return new CreateProjectResponse(fullySuccessful, $"Project file {projectFi.FullName} and Content file {contentFi.FullName} were created");
    }


    private bool GenerateProjectFile(string projectFullName, string[] nugetReferences, string internalsVisibleTo = "")
    {
        string packageReferences = "";
        if (nugetReferences.Any())
        {
            packageReferences = string.Join(Environment.NewLine,
                nugetReferences.Select(x => $"    <PackageReference Include=\"{x}\" Version=\"*\" />"));
        }

        var projectContent = projectTemplate.Replace("{packageReferences}", packageReferences);
        string internalsVisibleToSection = "";
        if (internalsVisibleTo.Length > 0)
        {
            internalsVisibleToSection = internalsVisibleTemplate.Replace("{internalsVisibleTo}", internalsVisibleTo);
        }
        projectContent = projectContent.Replace("{internalsVisibleToSection}", internalsVisibleToSection);
        //_fileSystem.File.WriteAllText(projectFullName, projContent);
        return CreateFile(projectFullName, projectContent); //_fileSystem.File.Exists(projectFullName);
    }

    private bool CreateFile(string fileFullName, string content)
    {
        _fileSystem.File.WriteAllText(fileFullName, content);
        return _fileSystem.File.Exists(fileFullName);
    }

    public bool CreateSolutionFile(string rootDirectoryPath, string projectName)
    {
        var solnFilePath = Path.Combine(rootDirectoryPath, projectName + ".slnx");
        bool successful = CreateFile(solnFilePath, projectSolutionTemplate.Replace("{projectName}", projectName));

        return successful;
    }


    private static string projectTemplate =
        """
        <Project Sdk="Microsoft.NET.Sdk">
          <PropertyGroup>
            <OutputType>Exe</OutputType>
            <TargetFramework>net10.0</TargetFramework>
            <LangVersion>latest</LangVersion>
            <Nullable>enable</Nullable>
          </PropertyGroup>

          <!-- Project references go here-->
          <ItemGroup>
          {packageReferences}
          </ItemGroup>

          {internalsVisibleToSection}
        </Project>
        """;
        
    private static string  projectSolutionTemplate = 
        """
        <Solution>
          <Configurations>
            <Platform Name="Any CPU" />
          </Configurations>
          <Folder Name="/Solution Items/">
            <File Path=".editorconfig" />
          </Folder>
          <Project Path="{projectName}/{projectName}.csproj" />
          <Project Path="{projectName}.NUnit/{projectName}.NUnit.csproj" />
        </Solution>
        """;

    private static string internalsVisibleTemplate =
        """
        <ItemGroup>
            <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
                <_Parameter1>{internalsVisibleTo}</_Parameter1>
            </AssemblyAttribute>
        </ItemGroup>
        """;

}

public class NUnitSamples
{
   public static readonly string NUnitSampleTest = // See https://rbovill.blogspot.com/2006/01/using-nunit.html
    """
    // See https://rbovill.blogspot.com/2006/01/using-nunit.html
    using NUnit.Framework;

    [TestFixture]
    public class SomeTester
    {
      // Format of a Test method, Try to put all the setup for the test in the test.
      // If necessary add private Setup/Initialise/Teardown methods to assist this 
      // rather than using the one listed above
      [Test]
      public void SomeTest1()
      {
      }
     
      // You can add test parameters to a test and use it to test multiple cases 
      [Test]
      [TestCase(5.0d, 0.0d, 3.0d)]
      [TestCase(5.0d, 1.0d, 3.0d)]
      [TestCase(5.0d, 0.0d, 5.0d)]
      public void SomeTest2(double fullLengthSecs, double startTimeSecs, int expectedValue)
      {
      }
    }   
    """;
}

December 4, 2022

Linqpad Notepad++ Hyperlink Extension

When searching through some files using LinqPad it can be useful to print out the results as a hyperlink that will open the file in Notepad++ at a particular line number.
public static class NotepadppExtension
{
    // Usage
    //string filePath = "X:/some/path/file.txt";
    //filePath.CreateNotePadppHyperLink(lineNumber);

    private const string NotePadppPath = @"C:\Program Files\Notepad++\notepad++.exe";
    private static bool onceOnly = false;

    public static Hyperlinq CreateNotepadppHyperLink(this string filePath, int lineNumber)
    {
        if (!onceOnly)
        {
            onceOnly = true;
            Debug.Assert(File.Exists(NotePadppPath), $"Notepad++.exe Path: \"{NotePadppPath}\" is wrong");
        }
        ProcessStartInfo psi = new ProcessStartInfo()
        {
            FileName = NotePadppPath,
            WorkingDirectory = Path.GetDirectoryName(NotePadppPath),
            //Arguments = " " + filePath + " -n" + lineNumber.ToString() + " ",
        };
        psi.ArgumentList.Add(filePath);
        psi.ArgumentList.Add("-n" + lineNumber.ToString());
        var filelink = new Hyperlinq(() => Process.Start(psi), filePath);

        return filelink;
    }
}
Usage
string filePath = .... ;
filePath.CreateNotePadppHyperLink(lineNumber);
A search files example in Linq
void Main()
{
	Directory.EnumerateFiles(
	@"X:\Backup\Documents\Journals\", 
	"*.log", SearchOption.AllDirectories)
		.SelectMany(file => TryFileReadLines(file).Select((text,n)=> 
                 new {Text=text,LineNumber=n+1, Link=file.CreateNotepadppHyperLink(n+1)}))
		.Where(line => 
		    //Regex.IsMatch(line.text, @"CallSearcherBase")  && 
		    line.Text.Contains("\"Search for this text\"", StringComparison.OrdinalIgnoreCase) )	
		.Dump("Matches found");
}

March 4, 2021

Search File Contents with LinqPad

Searching the contents of some file or files in LinqPad (a line at a time) in C# or even just finding a file:
Directory.EnumerateFiles(
  @"C:\Path\To\Search\Directory\", // Target of search
  "*.txt", // Specify file or files to search
  SearchOption.AllDirectories).  
  // SearchOption.AllDirectories - recusively search subdirectories
  // SearchOption.TopDirectoryOnly - search specified directory only
  // Next line records a line of text, the line number, and the file
  SelectMany(file => File.ReadAllLines(file).Select((text,n) => new {text,lineNumber=n+1,file})).
  // Here we search the text
  // Use Regex for the search OR use string methods Contains(), StartsWith, EndsWith ...
  Where(line => Regex.IsMatch(line.text, @"Searching for this text", RegexOptions.IgnoreCase)
                                            && line.text.Contains("ERROR") ).
  Dump("Matches found")
When debugging you can always use Skip() and Take() to reduce the number of lines searched.
A similar one but this uses a LinqPad HyperLinq object to use Notepad++ to open the file at the given line number.
// https://npp-user-manual.org/docs/command-prompt/
var notepadppPath = @"C:\Program Files (x86)\Tools\Notepad++\notepad++.exe";
Directory.EnumerateFiles(
   @"C:\Repos\",  // Directory
   "*.csproj", SearchOption.AllDirectories) // File names
   .SelectMany(file => File.ReadAllLines(file).Select((text, n) =>
		new
		{   
			file = new Hyperlinq(() => Process.Start(notepadppPath, 
				file+" -n"+(n+1).ToString()), file),
			lineNumber = n + 1,
			text
		}))
   .Where(x => // WHat you are looking for in the file
  // Regex.IsMatch(line.text, @"^.*tlpNotifySystemShutdown.*$"))
     x.text.Contains("DownloadProcess.Contracts") // 1.0.1
  || x.text.Contains("Foobar.Client")  // 13.15.0
  || x.text.Contains("DownloadProcess.Client") // 1.2.0
  || x.text.Contains("XxxZipCreator.")  // 5.1.0
  || x.text.Contains("Sloopy.Crial.Messages") // 1.6.3
   )
   .Dump("Matches found");
Here is another example:
Directory.EnumerateFiles(
  @"C:\Path\To\Search\Directory\",
  "*.cs", SearchOption.AllDirectories)
  .SelectMany(file => File.ReadAllLines(file).Select((text, n) => new { text, lineNumber = n + 1, file }))
  .Where(line => line.text.Contains("[CallerMemberName"))
  .Dump("Matches found")

August 28, 2013

LINQPad Command-Line and Scripting

LINQPad Command-Line and Scripting
Sample usage:
CALL C:\...\LinqPad\lprun.exe "C:\...\Queries\AttachDatabases.linq" DEV
This runs the given linq query. The sample is a "C# Program" and has a "Main" method taking "string[] args" as a paremeter. In this way the "DEV" string at the end of the line is passed as a parameter to the linq program.
Here is the sample linq script:
<Query Kind="Program" />

void Main(string[] args)
{
  string attachFolder = @"C:\Databases\";
  if ((args != null) && (args.Length == 1))
  {
  attachFolder = Path.Combine(attachFolder, args[0]);
  }
  Console.WriteLine("Attaching to databases in \'" + attachFolder + "\'"); 
  Console.WriteLine("");
  var server = @".\";
  var databaseNames = new[] { "XXX", "YYY", "ZZZ", "AAA" };

    using(var connection = new SqlConnection(
   string.Format("Server={0};Database=master;Trusted_Connection=True;", server)))
    {
    connection.Open();
  
    // attach the databases
    foreach(var database in databaseNames)
    {
      var dataFile = Path.Combine(attachFolder, database + "_Data.MDF");
      if (File.Exists(dataFile))
      {
        Console.WriteLine("Attaching {0}", database);
        var attachCommand = connection.CreateCommand();
        attachCommand.CommandText = "sp_attach_db  @dbName, @dataFileName, @logFileName";
        attachCommand.Parameters.AddWithValue("dbName", database);
        attachCommand.Parameters.AddWithValue("dataFileName", dataFile);
        attachCommand.Parameters.AddWithValue("logFileName", 
            Path.Combine(attachFolder, database + "_Log.LDF"));    
        attachCommand.ExecuteNonQuery();
      }
      else
      {
        Console.WriteLine("No data file for {0}", database);
      }
    }
      }
  
    Console.WriteLine("");
    Console.WriteLine("Press any key to continue ...");
    Console.ReadKey(false);
}
If you want the script to hang around a bit so that you can read the error messages then you can add the the following lines to the end
Console.WriteLine("");
Console.WriteLine("Press any key to continue ...");
Console.ReadKey(false);
This keeps the script alive until a key is pressed.