How would I use the code used to create powershell cmdlets in another c# method instead of a powershell script.
I have the following code:
public class Program
{
static void Main(string[] args)
{
var getCommand = new GetCommand { Text = "Hello World"};
//help needed here
}
}
[Cmdlet("Test", "Get")]
public class GetCommand : Cmdlet
{
[Parameter(Mandatory = true)]
public string Text { get; set; }
protected override void ProcessRecord()
{
WriteObject(Text);
}
}
Don't instantiate the GetCommand class - PowerShell will do that for you!
First, you'll need to spin up an instance of the PowerShell class to execute your command:
PowerShell ps = PowerShell.Create();
Then add a CommandInfo reference with the AddCommand method:
ps.AddCommand(new CmdletInfo("Test-Get", typeof(GetCommand)));
And then add your parameter argument:
ps.AddParameter("Text", "Hello World");
Now you can execute it (and collect the output) with the Invoke() method:
var output = ps.Invoke();
foreach(var obj in ouput)
{
Console.WriteLine("Output was: {0}", obj);
}
Extract the logic in a seperate class and call it directly. Use the cmdlet to be, well, just a shell around this new class.
This Seperation of Concerns (SoC) also enables easier unit tests and leads to an overall cleaner architecture.
Extracted Class Greeter.cs
public class Greeter {
public Greeter(string name) {
_Name = name;
}
private string _Name;
public string SayHello() {
return $"Hello {_Name}";
}
public string SayGoodBye() {
return $"So long {_Name}, and thanks for all the fish!";
}
}
CommandLet GetGreetingCommand.cs
[Cmdlet("Greeting", "Get")]
public class GetGreetingCommand : Cmdlet {
[Parameter(Mandatory = true)]
public string Name { get; set; }
protected override void ProcessRecord() {
var greeter = new Greeter(Name);
var greeting = greeter.SayHello();
WriteObject(greeting);
}
}
CommandLet GetGoodByeCommand .cs
[Cmdlet("GoodBye", "Get")]
public class GetGoodByeCommand : Cmdlet {
[Parameter(Mandatory = true)]
public string Name { get; set; }
protected override void ProcessRecord() {
var greeter = new Greeter(Name);
var goodBye = greeter.SayGoodBye();
WriteObject(goodBye);
}
}
Console Main.cs (or any other client-code of Greeter-class)
public static void main(string[] args) {
var greeter = new Greeter(args.FirstOrDefault());
Console.WriteLine(greeter.SayHello());
Console.WriteLine(greeter.SayGoodBye());
}
TestCase
public static void SayingHelloUsesName() {
var sut = new Greeter("Arthur");
var expected = "Hello Arthur";
var actual = sut.SayHello();
Assert.AreEqual(expected, actual);
}
The two concerns here are
- the actual BusinessLogic (Greeter.cs)
- interoperability with PowerShell, providing mechanisms to parameterize the cmdlet, etc. (Get*Command.cs). As you see, the cmdlets really only pass through the calls, while enabling use via PowerShell.
#Mathias R. Jessen ยด answer could be usefull, if you need to call third party cmdlets, but in most cases, there should be an appropriate (non-powershell) API for what you are trying to do.
Related
I'm trying to customize some of nUnits behaviour, however I'm constantly hitting a brick wall
because of nUnits heavy use of code reflection. Test methods (and also setup methods etc) are passed all the way down, deep into the framework, and are converted into a delegate at the latest step possible.
The classes I'm interested in are called TestCommands and only there the framework becomes functional.
For reference here is a snippet I found in nUnits source of the TestMethodCommand class which propably is the bread and butter test execution delegate.
public class TestMethodCommand : TestCommand
{
private readonly TestMethod testMethod;
private readonly object[] arguments;
public TestMethodCommand(TestMethod testMethod) : base(testMethod)
{
this.testMethod = testMethod;
this.arguments = testMethod.Arguments;
}
public override TestResult Execute(TestExecutionContext context)
{
object result = InvokeTestMethod(context); // missing a branch deciding about sync vs. async
// missing some code that checks object against "expected result"
return context.CurrentResult;
}
private object InvokeTestMethod(TestExecutionContext context)
{
return testMethod.Method.Invoke(context.TestObject, arguments);
}
}
I'm puzzled why nUnit couldn't wrap the test method into an Func<object> way way sooner and just pass the context along. As it stands for now if I don't have a MethodInfo nUnit can't run it.
In case you wonder, here is an example of a thing I want to do but I ran into the same problem in other instances as well.
[Scenario(When: "Device Registration reads out PCB Type",
Then: "Device Type might change")]
public void Identifier_Changes_Are_Recognized()
{
var changedType = reference.ChangeType(DeviceType.Terminal);
var changedID = reference.ChangeID(123456);
Assert.Multiple(() =>
{
AssertIsSameDevice(reference, changedType);
AssertIsDifferentDevice(reference, changedID);
});
}
This scenario attribute is supposed to print a small description like so.
public void RunBeforeTest()
{
var text = new MultiLineText
("Scenario:",
"\tGiven:\t" + When,
"\tThen:\t" + Then,
"-------------\n"
);
Console.WriteLine(text);
}
I reaaallly want to tell nUnit "Look, here is an action, please run it" but for the time beeing this seems very hard to achieve. Did anyone else here run in these kinds of problems?
Are there possibly ways to achieve what I'm trying to do? Maybe create my own TestCommand, but as I mentioned, these objects only get created very deep into the framework.
OP here (Writing from my home account)
I looked more into this and actually found a working solution:
public class ArbitraryCodeExecutionWrapper : DelegatingTestCommand
{
public ArbitraryCodeExecutionWrapper(TestCommand innerCommand) : base(innerCommand)
{
}
public Action<TestExecutionContext> BeforeTest { get; init; } = _ => { };
public Action<Test, TestResult> AfterTest { get; init; } = (_,_) => { };
public override TestResult Execute(TestExecutionContext context)
{
BeforeTest(context);
var result = innerCommand.Execute(context);
AfterTest(context.CurrentTest, result);
return result;
}
}
public class NUnitTestCommandWrapperAttribute : Attribute, IWrapTestMethod
{
protected virtual void BeforeTest(TestExecutionContext context)
{
}
protected virtual void AfterTest(Test test, TestResult result)
{
}
public TestCommand Wrap(TestCommand command)
=> new ArbitraryCodeExecutionWrapper(command)
{
BeforeTest = BeforeTest,
AfterTest = AfterTest
};
}
public class ScenarioAttribute : NUnitTestCommandWrapperAttribute
{
public string When { get; init; } = "";
public string Then { get; init; } = "";
protected override void BeforeTest(TestExecutionContext context)
{
var text = new MultiLineText
("Scenario:",
"\tGiven:\t" + When,
"\tThen:\t" + Then
);
Console.WriteLine(text);
}
protected override void AfterTest(Test test, TestResult result)
{
Console.WriteLine("After Test");
}
}
[TestFixture]
public class TestCodeExecution
{
[Test]
[Scenario(When = "nUnit Comes here",
Then = "Print Hello World")]
public void Try_Out_Code_Execution()
{
Console.WriteLine("Hello World");
}
}
public class MultiLineText
{
private List<string> items = new();
public static implicit operator string(MultiLineText text) => text.ToString();
public MultiLineText(params string[] lines)
{
items = lines.ToList();
}
public override string ToString() => string.Join("\n", items);
}
I'm writing my first PS Module in C# over the past few days and this is extremely new to me. I had some trouble coming up with this to be honest (maybe not enough caffeine) but I've come to a solution now however I'm hoping someone may have some more elegant ideas?
i'm finding some of the documentation regarding powershell to be a little vague, at least with regard to creating modules in C#. Embedding C# in PowerShell, no problem, running powershell code within C#, also tons of information, but information on writing PS modules in C# seems scarce, or I'm looking in the wrong places?
Enough chatter, here's my situation. First, I have a quick and dirty new and get cmdlet sample below.
[Cmdlet(VerbsCommon.New, "TestItem")]
[OutputType(typeof(TestItem))]
public class NewItem : Cmdlet
{
[Parameter(Position = 0)]
public string FriendlyName
{
get { return friendlyname; }
set { friendlyname = value; }
}
private string friendlyname;
[Parameter(Position = 1)]
public string Name
{
get { return name; }
set { name = value; }
}
private string name;
[Parameter(Position = 2)]
public int ID
{
get { return id; }
set { id = value; }
}
private int id;
private TestItem item;
protected override void BeginProcessing()
{
item = new TestItem();
}
protected override void ProcessRecord()
{
item.Name = name;
item.FriendlyName = friendlyname;
item.ID = id;
}
protected override void EndProcessing()
{
WriteObject(item);
}
}
[Cmdlet(VerbsCommon.Get, "TestItem")]
[OutputType(typeof(TestItem))]
public class GetItem : Cmdlet
{
[Parameter(Position = 0)]
public string[] FriendlyName
{
get { return friendlyname; }
set { friendlyname = value; }
}
private string[] friendlyname;
[Parameter(Position = 1)]
public List<TestItem> Item { get; set; }
[Parameter(ValueFromPipeline = true)]
public PSObject InputObject
{
set { inputObject = value; }
get { return inputObject; }
}
private PSObject inputObject;
private List<TestItem> item;
protected override void BeginProcessing()
{
item = new List<TestItem>();
}
protected override void ProcessRecord()
{
WriteVerbose("processing pipline");
if (inputObject != null)
{
WriteObject(inputObject.ToClassObject<TestItem>());
}
}
protected override void EndProcessing()
{
WriteObject(item);
}
}
Then I have my quick and dirty sample Object class
public class TestItem
{
public TestItem()
{ }
public string Name { get; set; }
public string FriendlyName { get; set; }
public int ID { get; set; }
}
And now this is where I'm looking for feedback. From the above we can see that the new-item creates an item, and when passed to get-item via the pipeline it's passed as a PSObject. My goal is to turn it back into the class object it started as, but I need to be able to handle Class/type dynamically as I intend to use this as a helper for a project I'm working on. I'm really just looking for feedback as I feel like there's a more elegant solution here?
public static class Helper
{
public static T ToClassObject<T>(this PSObject pso) where T : class, new()
{
try
{
T obj = new T();
foreach (PSPropertyInfo psPropertyInfo in pso.Properties)
{
foreach (var prop in obj.GetType().GetProperties())
{
try
{
if (psPropertyInfo.Name == prop.Name)
{
PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);
propertyInfo.SetValue(obj, Convert.ChangeType(psPropertyInfo.Value, propertyInfo.PropertyType), null);
}
}
catch
{
continue;
}
}
}
return obj;
}
catch
{
return null;
}
}
}
OK, what you are working on is called binary modules / binary cmdlets, that probably a word for googling (it gets me a good number of articles about that). You can also find a bunch of real life examples on Powershell Core github, for example here.
From the other side it doesn't seem to be a very common way of creating cmdlets for powershell though, and in my opinion are much more complicated than pure PS code.
Regarding to your question, as I understand your Get-Item cmdlet is getting PSObject from the pipeline, and you need to convert it to custom type using input property names. If so you probably can use ValueFromPipelineByPropertyName property instead of ToClassObject. I'm not any kind of expert in C# or binary module writing, so I'll give my example in pure PS, I hope it's not a huge problem to convert it to C#
function Get-Column { [CmdletBinding()]param(
[parameter(ValueFromPipelineByPropertyName=$true)][String]$columnName,
[parameter(ValueFromPipelineByPropertyName=$true)][String]$type
)
Process { Write-Output (New-Object System.Data.DataColumn #($columnName, $type))}
}
$ObjectList = #(
New-Object psobject -Property #{columnName="FirstName"; type="String"}
New-Object psobject -Property #{columnName="LastName"; type="String"}
New-Object psobject -Property #{columnName="Age"; type="Int32"}
)
$ObjectList | Get-Column | Format-Table
So that cmdlet collects parameter values from input object and pass it to your custom class constructor. Also, I don't really need Begin and End blocks, probably those are redundant in your code as well.
As Mike Twc says, you are creating binary cmdlets. They are less frequently used and I, personally, think it is a shame because I see so many "PowerShell" scripts that look like C#, and binary cmdlets allow for native, more strongly-typed use in other C# applications. So kudoos for trying.
I think your major problem is what type you are specifying where. For elegance, you really don't need to be so verbose in the way you write your properties.
So here is a more elegant re-write that should solve your issue:
[Cmdlet(VerbsCommon.New, "TestItem")]
[OutputType(typeof(TestItem))]
public class NewItem : Cmdlet
{
[Parameter(Position = 0)]
public string FriendlyName { get; set; }
[Parameter(Position = 1)]
public string Name { get; set; }
[Parameter(Position = 2)]
public int ID { get; set; }
protected override void ProcessRecord()
{
var item = new TestItem();
item.Name = Name;
item.FriendlyName = Friendlyname;
item.ID = ID;
WriteObject(item);
}
}
[Cmdlet(VerbsCommon.Get, "TestItem")]
[OutputType(typeof(TestItem))]
public class GetItem : Cmdlet
{
// what is the point of this parameter since it is never used?
// Is it supposed to filter? If so, I would suggest using
// `Where-Object FriendlyName -In "MyName1","MyName2","MyName3"`
// instead of trying to write your own...
[Parameter(Position = 0)]
public string[] FriendlyName { get; set; }
// This parameter is unused and, likely, is what you
// intend for the InputObject parameter
//[Parameter(Position = 1)]
//public List<TestItem> Item { get; set; }
// Whatever this type is, because it is ValueFromPipeline,
// is what the pipeline input will be converted to
[Parameter(ValueFromPipeline = true)]
public TestItem InputObject { get; set; }
private List<TestItem> items;
protected override void BeginProcessing()
{
items = new List<TestItem>();
}
protected override void ProcessRecord()
{
WriteVerbose("processing pipline");
if (InputObject != null)
{
// do you actually mean `items.add(InputObject);`?
WriteObject(InputObject);
}
}
protected override void EndProcessing()
{
// this is empty because it never has elements added?
WriteObject(items, enumerateCollection: false);
}
}
Now another comment: The Get- verb, and you can see this in MS Doc, should act similarly to the New- verb in that it gets the instances already in the system, but is different in that it does not instantiate new instances. I am not fully sure what you are trying to do, since you seem to hand off the input object to later in the pipeline, which it otherwise would already be going to without the cmdlet--unless you are doing what my comments suggest and gathering them all into a list, and outputing the list
I can't get Moq to mock an object that gets created in a static method.
Here is my moq and code
code:
public interface IConfigHelper
{
string GetConfiguration(string sectionName, string elementName);
}
public class ConfigHelper : IConfigHelper
{
public ConfigHelper() { }
public virtual string GetConfiguration(string sectionName, string elementName)
{
string retValue = String.Empty;
//Does things to get configuration and return a value
return retValue;
}
}
public class myRealClass
{
public myRealClass(){}
public string myworkingMethod()
{
var retValue = String.Empty;
retValue = utilSvc.GetConfigurationValue();
return retValue;
}
}
public static class utilSvc
{
public static string GetConfigurationValue()
{
ConfigHelper configUtil = new ConfigHelper(); //NOT BEING MOCKED
return configUtil.GetConfiguration("sectionName/sectionElement", "ClinicalSystem");
}
}
the Test using Moq
[TestFixture(TestName = "Tests")]
public class Tests
{
private Mock<IConfigHelper> configHelperMOCK;
[SetUp]
public void Setup()
{
configHelperMOCK = new Mock<IConfigHelper>();
}
[Test]
public void serviceIsBPManagementForValidSource()
{
//Arrange
string sectionName = "sectionName/sectionElement";
string clinicalElementName = "ClinicalSystem";
string clinicalElementValue = "Zedmed";
configHelperMOCK.Setup(s => s.GetConfiguration(sectionName, clinicalElementName)).Returns(clinicalElementValue);
//act
// the call to myRealClass
//assert
// test assertions
}
}
The issue that I am having is with this line:
ConfigHelper configUtil = new ConfigHelper(); //NOT BEING MOCKED
I cannot get the moq to Mock the object.
I do not want the code to read the config file. I wish to moq away this instance of ConfigHelper
You can't wrap the static class/method but you can redirect it
public static class UtilSvc
{
static UtilSvc()
{
CreatorFunc = () => new ConfigHelper();
}
public static Func<IConfigHelper> CreatorFunc { get; set; }
public static string GetConfigurationValue()
{
var configUtil = CreatorFunc();
return configUtil.GetConfiguration("sectionName/sectionElement",
"ClinicalSystem");
}
}
and then in the test
//...
private Mock<IConfigHelper> configHelperMOCK;
[SetUp]
public void Setup()
{
configHelperMOCK = new Mock<IConfigHelper>();
UtilService.CreatorFunc = () => configHelperMOCK.Object;
}
//...
You cannot mock static class. I would rather propose to inject that IConfigHelper into the myRealClass. That is the usual way how to decouple dependencies and use DI.
public class myRealClass
{
private IConfigHelper _configHelper;
public myRealClass(IConfigHelper configHelper)
{
_configHelper = configHelper;
}
public string myworkingMethod()
{
var retValue = String.Empty;
retValue = _configHelper.GetConfigurationValue();
return retValue;
}
}
Avoid coupling your code to static classes, which in most cases cause you code be to difficult to maintain and test.
Follow the Explicit Dependencies Principle
Methods and classes should explicitly require (typically through
method parameters or constructor parameters) any collaborating objects
they need in order to function correctly.
Give the article a read. It is short and very informative.
If you want to keep the static class then you wrap the static class behind an abstraction.
public interface IUtilSvc {
string GetConfigurationValue();
}
public class utilSvcWrapper : IUtilSvc {
public string GetConfigurationValue() {
return utilSvc.GetConfigurationValue(); //Calling static service
}
}
Or another option is that utlSvc does not have to be static if can be injected into dependent classes
public class utilSvc : IUtilScv {
private readonly IConfigHelper configUtil;
public utilSvc(IConfigHelper configHelper) {
configUtil = configHelper;
}
public string GetConfigurationValue() {
return configUtil.GetConfiguration("sectionName/sectionElement", "ClinicalSystem");
}
}
Inject the IUtilScv into the dependent class so that it is no longer dependent on static class.
public class myRealClass {
private readonly IUtilScv utilSvc;
//Explicit dependency inject via constructor
public myRealClass(IUtilScv utilSvc) {
this.utilSvc = utilSvc;
}
public string myworkingMethod() {
var retValue = utilSvc.GetConfiguration();
return retValue;
}
}
In that case you don't even need IConfigHelper when testing as it has also been abstracted away. And you only need to mock the dependencies needed for the test.
[TestFixture(TestName = "Tests")]
public class Tests {
private Mock<IUtilScv> utilScvMOCK;
[SetUp]
public void Setup() {
utilScvMOCK = new Mock<IUtilScv>();
}
[Test]
public void serviceIsBPManagementForValidSource() {
//Arrange
var expectedClinicalElementValue = "Zedmed";
utilScvMOCK
.Setup(s => s.GetConfiguration())
.Returns(expectedClinicalElementValue)
.Verifiable();
var sut = new myRealClass(utilScvMOCK.Object);
//Act
var actualClinicalElementValue = sut.myworkingMethod();
//Assert
configHelperMOCK.Verify();
Assert.AreEqual(expectedClinicalElementValue, actualClinicalElementValue);
}
}
I am busy developing a class library project in C# to be reused and attached to different projects in future. It will mainly be used for Table Valued Parameters. My question is, how do I pass a SQL connection to it? The connection will be instantiated in another (main project) that the .dll gets attached to.
I currently have a Class Library Project, and have a Console Application Project created in the same solution for testing purposed.
One last requirement is that I don't want to use ConfigurationManager as the connection string will not be stored in app.config or web.config and by default the queries must be passed back to the calling application.
I've come accross a couple of links like the one below, but nothing I can really use:
Sharing a connection string
Please excuse the noobness, I am 7 weeks into professional programming.
In your dll, simply require an IDbConnection or IDbCommand. All the method is then properly abstracted against the interfaces for the data access.
For example:
In your shared dll
public static int LookUpIntForSomething(IDbConnection connection)
{
using (var command = connection.CreateCommand())
{
// use command.
}
}
In your calling app
using (var connection = new SqlConnection("ConnectionString"))
{
var int = DbQueries.LookupIntForSomething(connection);
}
This is excellent example for dependency injection. I would recommend using enterprise library unity for this kind of stuff. In your data access layer library I would define interface:
public interface IConnectionProvider {
string ConnectionString { get; }
}
public interface IAccountProvider {
Account GetAccountById(int accountID);
}
internal class AccountProvider : IAccountProvider {
private IConnectionProvider _connectionProvider;
public AccountProvider(IConnectionProvider connectionProvider) {
if (connectionProvider == null) {
throw new ArgumentNullException("connectionProvider");
}
_connectionProvider = connectionProvider;
}
public Account GetAccountById(int accountID) {
Account result;
using(var conn = new SqlConnection(connectionProvider)) {
// retrieve result here
}
return result;
}
}
public static class Bootstrapper {
public static void Init() {
ServiceLocator.AddSingleton<IAccountProvider, AccountProvider>();
}
}
Then in any assembly using your data access library you can define implementation for IConnectionProvider, like this:
internal class WebConnectionProvider : IConnectionProvider {
public string ConnectionString { get { return "Server=..."; } }
}
internal static class WebBootstrapper {
public static void Init() {
Bootstrapper.Init();
ServiceLocator.AddSingleton<IConnectionProvider, WebConnectionProvider>();
}
}
And anywhere after you call WebBootstrapper.Init() in your assembly you can use:
var accountProvider = ServiceLocator.Resolve<IAccountProvider>();
accountProvider.GetAccountById(1);
Service locator:
using System;
using Microsoft.Practices.Unity;
public class ServiceLocator {
private IUnityContainer m_Container = new UnityContainer();
public void Add<TFrom, TTo>() where TTo : TFrom {
m_Container.RegisterType<TFrom, TTo>();
}
public void BuildUp<T>(T instance) {
m_Container.BuildUp<T>(instance);
}
public void BuildUp(Type type, object instance) {
m_Container.BuildUp(type, instance);
}
public void AddSingleton<TFrom, TTo>() where TTo : TFrom {
m_Container.RegisterType<TFrom, TTo>(new ContainerControlledLifetimeManager());
}
public void AddInstance<T>(T instance) {
m_Container.RegisterInstance<T>(instance);
}
public T Resolve<T>() {
return m_Container.Resolve<T>();
}
private static ServiceLocator m_Instance;
public static ServiceLocator Instance {
get { return m_Instance; }
}
static ServiceLocator() {
m_Instance = new ServiceLocator();
}
}
if i understand your requirements correctly,I'm not sure that i do, i would setup a static struct as such
public static struct ConnectionString
{
public int ID;
public string Connection;
public override string ToString()
{
return Connection;
}
public static ConnectionString DataBase1 = new ConnectionString{ ID = 1 , Connection = "YourConnectionStringhere"};
public static ConnectionString DataBase2 = new ConnectionString{ ID = 2 , Connection = "YourConnectionString2here"};
}
and then use it as such
public void SomeMethod()
{
var I = ReferencedDll.DoSomething(ConnectionString.DataBase1.ToString());
}
or
public void SomeMethod()
{
var ClassFromDll = new ReferencedDll.SomeClass(ConnectionString.DataBase1.ToString());
ClassFromDll.DoSomething();
}
of course this leaves your connection strings hard coded which is not ideal
I would like to check the ApplicationExitCode for my C# Console Program under test. Below is my NUnit test method. I am uncertain what to replace the question marks with.
My testing class method:
[Test]
public void ExitApplicationWithZeroOnNoErrors()
{
string[] arguments = { "--version=43" };
var program = new Program(arguments);
Assert.AreEqual(Utility.Status.Success, ?????);
}
My main application Program.cs.
public class Program
{
public int? Version { get; private set; }
[STAThread]
public static int Main(string[] arguments)
{
var program = new Program(arguments);
return (int)Utility.Status.Success;
}
public Program(IEnumerable<string> arguments)
{
var parameters = new OptionSet()
{
{"v|version=", "Client version number.", (int v) => Version = v},
};
parameters.Parse(arguments);
}
}
As an aside I am using NDesk.Options class for my parameter gathering. I attempting to write Unit Tests and utilize TDD for this project.
In order to check the exit code as would be returned to the OS, you should check the value returned from Main. So you will have to run Main:
var returnValue = Program.Main(arguments);
Assert.AreEqual(Utility.Status.Success, returnValue);