DEV Community

Alastair Lundy
Alastair Lundy

Posted on

Looking for Feedback on my .NET library - CliInvoke

Hi everyone!

Since June I've been working on version 2 of one of my .NET libraries, CliInvoke - a library focussed on making running external processes from .NET safer and easier. It started out as an alternative to CliWrap but has diverged in strategies and direction, with CliInvoke focussing more on separation of concerns and Dependency Injection support.

From my perspective it's fair to say that, although well intentioned, CliInvoke version 1 hasn't withstood the test of time from an API surface and ergonomics perspective.

I've tried to learn from version 1's failings and am now looking for feedback before I release version 2. The goal of CliInvoke remains the same as before but with improved ergonomics and a cleaner API surface.

Here's a quick overview of what can be done with CliInvoke v2 pre-release versions, though the Factory Interface approach isn't yet in a published version.

Command Execution

Factory Interface Approach


using AlastairLundy.CliInvoke.Core.Factories;
using AlastairLundy.CliInvoke.Core;
using AlastairLundy.CliIinvoke;

using Microsoft.Extensions.DependencyInjection;

// Dependency Injection setup code ommitted for clarity

IProcessConfigurationFactory processConfigFactory = serviceProvider.GetRequiredService<IProcessConfigurationFactory>();

IProcessConfigurationInvoker invoker = serviceProvider.GetRequiredService<IProcessConfigurationInvoker>();

ProcessConfiguration configuration = processConfigFactory.Create("path/to/exe", "arguments");

ProcessResult result = await invoker.ExecuteAsync(configuration, CancellationToken.None);
Enter fullscreen mode Exit fullscreen mode

Builder Configuration

This approach is more complex but allows for greater configuration of ProcessConfiguration.

using AlastairLundy.CliInvoke;
using AlastairLundy.CliInvoke.Builders;
using AlastairLundy.CliInvoke.Core;
using AlastairLundy.CliInvoke.Core.Builders;

using Microsoft.Extensions.DependencyInjection;

  //Namespace and class code ommitted for clarity 

  // Dependency Injection setup code ommitted for clarity

  IProcessConfigurationInvoker _processConfigInvoker = serviceProvider.GetRequiredService<IProcessConfigurationInvoker>();

  // Fluently configure your Command.
  IProcessConfigurationBuilder builder = new ProcessConfigurationBuilder("Path/To/Executable")
                            .WithArguments(["arg1", "arg2"])
                            .WithWorkingDirectory("/Path/To/Directory");

  // Build it as a ProcessConfiguration object when you're ready to use it.
  ProcessConfiguration config = builder.Build();

  // Execute the process through ProcessInvoker and get the results.
ProcessResult result = await _processConfigInvoker.ExecuteAsync(config);
Enter fullscreen mode Exit fullscreen mode

Buffered Command Execution

This is similar to Command Execution except with the Standard Output and Error redirected and saved to strings in the BufferedProcessResult class, which inherits from the base ProcessResult class.

Factory Interface Approach


using AlastairLundy.CliInvoke.Core.Factories;
using AlastairLundy.CliInvoke.Core;
using AlastairLundy.CliIinvoke;

using Microsoft.Extensions.DependencyInjection;

// Dependency Injection setup code ommitted for clarity

IProcessConfigurationFactory processConfigFactory = serviceProvider.GetRequiredService<IProcessConfigurationFactory>();

IProcessConfigurationInvoker invoker = serviceProvider.GetRequiredService<IProcessConfigurationInvoker>();

ProcessConfiguration configuration = processConfigFactory.Create("path/to/exe", "arguments");

BufferedProcessResult result = await invoker.ExecuteBufferedAsync(configuration, CancellationToken.None);
Enter fullscreen mode Exit fullscreen mode

Builder Configuration

using AlastairLundy.CliInvoke;
using AlastairLundy.CliInvoke.Builders;
using AlastairLundy.CliInvoke.Core;
using AlastairLundy.CliInvoke.Core.Builders;

using Microsoft.Extensions.DependencyInjection;

  //Namespace and class code ommitted for clarity 

  // Dependency Injection setup code ommitted for clarity

  IProcessConfigurationInvoker _processConfigInvoker = serviceProvider.GetRequiredService<IProcessConfigurationInvoker>();

  // Fluently configure your Command.
  IProcessConfigurationBuilder builder = new ProcessConfigurationBuilder("Path/To/Executable")
                            .WithArguments(["arg1", "arg2"])
                            .WithWorkingDirectory("/Path/To/Directory")
.RedirectStandardOutput(true)
.RedirectStandardError(true);

  // Build it as a ProcessConfiguration object when you're ready to use it.
  ProcessConfiguration config = builder.Build();

  // Execute the process through ProcessInvoker and get the results.
BufferedProcessResult result = await _processConfigInvoker.ExecuteBufferedAsync(config);
Enter fullscreen mode Exit fullscreen mode

Closing Thoughts

I'd like to get your thoughts and opinions/any feedback you have.
Is there anything you think I should change in the library or anything that you think needs adding?

Thanks in advance!

Top comments (0)