I'm building a DLL in C# that I will be consuming with several different projects - so far, I know of a WPF application and a (binary) PowerShell module. Because the core business logic needs to be shared across multiple projects, I don't want the PowerShell module itself to contain the core logic. I'd just like to reference my primary library.
I'm struggling to figure out how to implement a clean logging solution in my core DLL that will be accessible via PowerShell's WriteVerbose() method. Without this, I can provide verbose output to PowerShell about PowerShell-specific things, but I can't provide any verbose output about "waiting for HTTP request" or other features that would be in the core DLL.
Here's a simple example of what I'm trying to do:
using System;
using System.Threading;
namespace CoreApp
{
public class AppObject
{
public AppObject() {}
public int DoStuffThatTakesForever()
{
// Assume logger is a logging object - could be an existing
// library like NLog, or I could write it myself
logger.Info("Doing step 1");
Thread.Sleep(5000);
logger.Info("Doing step 2");
Thread.Sleep(5000);
logger.Info("Doing step 3");
Random r = new Random();
r.Next(0, 10);
}
}
}
////////////////////////////////////////////////////////////
// Separate VS project that references the CoreApp project
using System.Management.Automation;
using CoreApp;
namespace CoreApp.PowerShell
{
[Cmdlet(VerbsCommon.Invoke, "ThingWithAppObject"]
[OutputType(typeof(Int32))]
public class InvokeThingWithAppObject : Cmdlet
{
[Parameter(Position = 0)]
public AppObject InputObject {get; set;}
protected override void ProcessRecord()
{
// Here I want to be able to send the logging phrases,
// "Doing step 1", "Doing step 2", etc., to PowerShell's
// verbose stream (probably using Cmdlet.WriteVerbose() )
int result = InputObject.DoStuffThatTakesForever();
WriteObject(result);
}
}
}
How can I provide verbose PowerShell verbose output without tightly binding the core library with the PowerShell module?
I'm definitely open to other solutions, but here's how I ended up solving it:
In the core library, I created an ILogger interface with methods for Info, Verbose, Warn, etc. I created a DefaultLogger class that implemented that logger (by writing everything to the attached debugger), and I gave this class a static singleton instance.
In each method that I wanted logged, I added an optional ILogger parameter, and added a line to use the default logger if necessary. The method definitions now look like this:
public int DoSomething(ILogger logger = null)
{
logger = logger ?? MyAppLogger.Singleton;
// Rest of the code
Random r = new Random();
return r.Next(0, 10);
}
I had to do this for each method because the PSCmdlet.WriteVerbose() method expects to be called from the currently running cmdlet. I couldn't create a persistent class variable to hold a logger object because each time the user ran a cmdlet, the PSCmdlet object (with the WriteVerbose method I need) would change.
Finally, I went back to the PowerShell consumer project. I implemented the ILogger class in my base cmdlet class:
public class MyCmdletBase : PSCmdlet, ILogger
{
public void Verbose(string message) => WriteVerbose(message);
public void Debug(string message) => WriteDebug(message);
// etc.
}
Now it's trivial to pass the current cmdlet as an ILogger instance when calling a method from the core library:
[Cmdlet(VerbsCommon.Invoke, "ThingWithAppObject"]
[OutputType(typeof(Int32))]
public class InvokeThingWithAppObject : MyCmdletBase
{
[Parameter(Mandatory = true, Position = 0)]
public AppObject InputObject {get; set;}
protected override void ProcessRecord()
{
int result = InputObject.DoSomething(this);
WriteObject(result);
}
}
In a different project, I'll need to write some kind of "log adapter" to implement the ILogger interface and write log entries to NLog (or whatever logging library I end up with).
The only other hiccup I ran into is that WriteVerbose(), WriteDebug(), etc. cannot be called from a different thread than the main thread the cmdlet is running on. This was a significant problem, since I'm making async Web requests, but after banging my head on the wall I decided to just block and run the Web requests synchronously instead. I'll probably end up implementing both a synchronous and an async version of each Web-based function in the core library.
This approach feels a bit dirty to me, but it works brilliantly.
Related
I'm building a selenium test framework based on .Net Core and the team decided to go with xUnit. All's well and good everything has been going ok but for a while now, we've been trying to replicate the functionality of Java TestNG listeners without much luck.
I've been digging around the xunit git repo and found a few instances where some interfaces such ITestListener have been used. After digging deeper, I found that these listeners are from a package called TestDriven.Framework and I wanted to know exactly how would I use a test listener created using those interfaces?
So far this is my simple test listener that should write something when the test fails:
public class Listener
{
readonly int totalTests;
public Listener(ITestListener listener, int totalTests)
{
this.totalTests = totalTests;
TestListener = listener;
TestRunState = TestRunState.NoTests;
}
public ITestListener TestListener { get; private set; }
public TestRunState TestRunState { get; set; }
public void onTestFail(ITestFailed args)
{
Console.WriteLine(args.Messages);
}
}
Now, I know you can do this inside a tear down hook but remember, this is just a simple example and what I have in mind is something more complex. So to be precise, where/how exactly would I register the test to use this listener? In Java TestNg I would have #Listeners but in C# I'm not too sure.
Edit 1 so the example worked and managed to add it to my own project structure but when I try to use this
class TestPassed : TestResultMessage, ITestPassed
{
/// <summary>
/// Initializes a new instance of the <see cref="TestPassed"/> class.
/// </summary>
public TestPassed(ITest test, decimal executionTime, string output)
: base(test, executionTime, output) {
Console.WriteLine("Execution time was an awesome " + executionTime);
}
}
I'm having trouble registering this one, or if i'm even registering it right. As far as the examples go, I have found the actual message sinks but also found the actual test status data which i'm not exactly sure how to use.
I haven't worked with TestNG, but I did some quick reading and I think I see what you're after.
To demonstrate, I've created a very basic proof-of-concept implementation of the xUnit [IMessageSink] interface (https://github.com/xunit/abstractions.xunit/blob/master/src/xunit.abstractions/Messages/BaseInterfaces/IMessageSink.cs).
public class MyMessageSink : IMessageSink
{
public bool OnMessage(IMessageSinkMessage message)
{
// Do what you want to in response to events here.
//
// Each event has a corresponding implementation of IMessageSinkMessage.
// See examples here: https://github.com/xunit/abstractions.xunit/tree/master/src/xunit.abstractions/Messages
if (message is ITestPassed)
{
// Beware that this message won't actually appear in the Visual Studio Test Output console.
// It's just here as an example. You can set a breakpoint to see that the line is hit.
Console.WriteLine("Execution time was an awesome " + ((ITestPassed)message).ExecutionTime);
}
// Return `false` if you want to interrupt test execution.
return true;
}
}
The sink is then registered via an IRunnerReporter:
public class MyRunnerReporter : IRunnerReporter
{
public string Description => "My custom runner reporter";
// Hard-coding `true` means this reporter will always be enabled.
//
// You can also implement logic to conditional enable/disable the reporter.
// Most reporters based this decision on an environment variable.
// Eg: https://github.com/xunit/xunit/blob/cbf28f6d911747fc2bcd64b6f57663aecac91a4c/src/xunit.runner.reporters/TeamCityReporter.cs#L11
public bool IsEnvironmentallyEnabled => true;
public string RunnerSwitch => "mycustomrunnerreporter";
public IMessageSink CreateMessageHandler(IRunnerLogger logger)
{
return new MyMessageSink();
}
}
To use my example code, just copy the classes into your test project (you'll also need to add a reference to the xunit.runner.utility NuGet package). The xUnit framework will automagically discover the IRunnerReporter--no need to explicitly register anything.
If this seems like it's headed in the right direction, you can find a lot more info in the xUnit source code. All of the interfaces involved are well-documented. There are a few existing implementations in the xunit.runner.reporters namespace. AssemblyRunner.cs also demonstrates one possible method for dispatching the different event types to individual handlers.
Edit 1
I've updated the implementation of MyMessageSink (above) to demonstrate how you might listen for an ITestPassed message. I also updated the link embedded in that code snippet--the previous link was to implementations, but we should really use these abstractions.
The if (message is IMessageType) pattern is pretty crude, and won't scale well if you want to listen for many different message types. Since I don't know your needs, I just went with the simplest thing that could possibly work--hopefully it's enough that you can improve/extend it to fit your needs.
I've defined the following attribute:
class MyAttribute: RouteAttribute, IActionModelConvention, IActionHttpMethodProvider
{
public IEnumerable<string> HttpMethods { get; }
public MyAttribute(string template) : base(UpdateTemplate(template))
{
HttpMethods = new[] { HttpMethod.Post.Method };
}
public void Apply(ActionModel action)
{
Foo();
}
}
The idea is to run Foo() without the need to use reflection and manually look for methods with the provided attribute.
Now, MVC will run Apply(action) only after the web application got its first request. I'm looking for a solution which will cause a method to run inside the attribute context without the need to find it by reflection and run a Foo().
Foo() does not have to be necessarily inside the attribute.
Any solution that allows me to have the instance of the attribute and on the startup of my application will be greeted.
Edit: For clarification, what I am trying to achieve is to use MVC's internal mechanism that searches for classes which implement certain interfaces and runs a certain function they implement. For an example here I used IActionModelConvention to run Foo() when using said mechanism. The problem here is that Foo() will run only after the routing mechanism is fired which only happens after a request was sent to the application. I'm looking for something to run my code as the application starts and to have my attribute's instance so I can pass Foo(this.someAttributeField) to it
Is there any way for the default Functions class that comes in WebJob projects to be internal? We are using a job activator to inject via Unity some dependencies that are internal, which requires that the Functions class also be internal. When running the web job, we are seeing the following error:
No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. config.UseServiceBus(), config.UseTimers(), etc.).
When we make all the dependencies public, it works fine, so I know there's nothing wrong with my triggers or my job host config.
Here's my Program class:
class Program
{
static void Main()
{
var config = new JobHostConfiguration
{
JobActivator = new Activator(new UnityContainer())
};
config.UseServiceBus();
var host = new JobHost(config);
host.RunAndBlock();
}
}
Here's a simplified version of my Functions class:
internal class Functions
{
private readonly IMyInternalDependency _dependency;
public Functions(IMyInternalDependency dependency)
{
_dependency = dependency;
}
public function DoSomething([ServiceBusTrigger("my-queue")] BrokeredMessage message)
{
// Do something with the message
}
}
You must make the Functions class public. That appears to be just how Azure WebJobs works. You don't need to expose your concrete internal classes publicly. Just the interfaces:
public interface IDoStuffPublically
{
void DoSomething();
}
interface IDoStuffInternally
{
void DoSomething();
void DoSomethingInternally();
}
class DoStuff : IDoStuffPublically, IDoStuffInternally
{
public void DoSomething()
{
// ...
}
public void DoSomethingInternally()
{
// ...
}
}
And then your Functions class:
public class Functions
{
public Functions(IDoStuffPublically stuff)
{
_stuff = stuff;
}
private IDoStuffPublically _stuff;
// ...
}
And Unity will do something like this:
var job = new Functions(new DoStuff());
Dave commented:
It's frustrating that I cannot simply set the internals visible to the WebJob SDK...
You might be able to accomplish this... miiiiiiiiiight be able to...
There is a way for an assembly or executable to grant another assembly the permission to access internal members. I've done this before on a class library to allow my unit tests to call internal methods on a class as part of setting up a unit test.
If you know which assembly in Azure WebJobs actually creates the instance of your Functions class, and the assembly that invokes the methods on that class, you could white list those assemblies.
Crack open AssemblyInfo.cs and add one or more lines:
[assembly: InternalsVisibleTo("Microsoft.Azure.Something.Something")]
Reference: InternalsVisibleToAttribute class
Related reading: .Net Tips – using InternalsVisibleTo attribute to help testing non-public methods
I'm not sure which assemblies you would need to add, though.
When using Triggers with the Webjob SDK, you never register the functions to be executed.
When the jobhost starts (new JobHost(config).RunAndBlock()), it discoverers the functions to be executed based on parameter attributes.
Let's have a look at your code:
var config = new JobHostConfiguration
{
JobActivator = new Activator(new UnityContainer())
};
config.UseServiceBus();
Because you specify that you want to use servicebus, when the jobhost starts, it will discover and register (index) all the functions that have a parameter with the ServiceBusTrigger attribute.
I assume that the SDK uses something like MemberInfo.GetCustomAttributes to index the functions so don't know if it is (possible and) easy to get attributes from an internal class.
This is an example console application (it will run fine after adding the Unity NugGet package) that seems to show a bug in Unity:
using System;
using Microsoft.Practices.Unity;
class GC { public static UnityContainer Container = new UnityContainer();}
class Program
{
static void Main(string[] args)
{
GC.Container.RegisterType<MyView>();
var myView = GC.Container.Resolve<MyView>();
Console.ReadLine();
}
}
public class MyClassDesign: MyClass{}
public class MyClass: VMBase<MyClass, MyClassDesign>{}
public abstract class VMBase<TViewModel, TDesignVM> where TDesignVM:TViewModel
{
static VMBase()
{
if (!GC.Container.IsRegistered(typeof(TViewModel)))
GC.Container.RegisterType(typeof (TViewModel), typeof(TDesignVM));
}
}
public class MyView
{
public MyView(MyClass myClass)
{
Console.WriteLine("Bad: "+myClass.GetType().ToString());
Console.WriteLine("Good: "+GC.Container.Resolve<MyClass>().GetType());
}
}
The output is:
Bad: MyClass
Good: MyClassDesign
The resolved type is MyClass. But it should be MyClassDesign. (The static constructor runs prior to MyClass being resolved in the MyView class.)
How can I get Unity to allow me to setup my Mapping in the Static Constructor?
Note: When I changed this setup the UnityContainer with a file (instead of in code) it all works fine. But I would rather not be dependent on an external file for this. (I am making a reusable template that I don't want to have too many dependencies in.)
Why do you want to put the registration logic inside your view model at all? This couples your application code to the container which is never a good idea. Have a look at the concept of Composition roots.
All setup code for the DI container should be placed there.
This isnt really a bug with Unity. The issue is that the static ctor is not run until an instance is requested (at which point unity still does not know about MyClassDesign). Which means that Unity has already started creating an instance of MyClass to fulfill the request. Any subsequent calls to GC.Container.Resolve<MyView>(); will result in the output you expect. As Sebastian Weber suggests, putting all your setup code in a completely seperate location (so your classes are not dependent on a specific DI container) is the best option.
Ok guys, this is very basic stuff I think.
I'm only getting familiar with C# and object oriented languages.
So here is a basic project I'm developping :
namespace MyProject
{
class MyProject
{
public Logger Logs = new Logger();
public WebServer Web = new WebServer();
static void Main()
{
// blabla
}
}
}
The logger is there to simply create a log file and when its "Add(string str)" method is called, it adds a line in the log file.
The webserver is my own webserver, opening a socket etc ...
I'd like my webserver to be able to use my Add method of the logger... how am I supposed to achieve that ?
Inside my webserver class, using something like "MyProject.Logs.Add(string str)" doesn't seem to work.
So how can I reference it ?
I don't want to create a new instance of my Logger class inside my webserver, because I want to write everyting in the same text file (I might have more module running than just a webserver as well).
So I'd like to use the instance I created in my Main method, inside my Webserver class...
I hope this makes sense ...
A couple of suggestions:
Logs and Web should be private. Those are implementation details of your class. They're nobody else's business.
Pass the instance of your logger to your web server:
public WebServer Web = new WebServer(Logs);
Your web server can now have its very own copy:
private Logger Logs {get;set;}
public WebServer(Logger logs)
{
Logs = logs;
}
public void DoSomething()
{
Logs.Add("I did something!");
}
MyProject.Logs.Add(string str) does not work because Logs member is not static, which means it can be different for different instances of your class (which are created by operator new). The compiler can't know which instance you are referring to if you don't specify it.
If you mark Logs as static, then it will work.
However, making it static makes your project less customizable (for example you will not be able to start two web-servers with two different log-files in the same program). That's why what John Saunders suggested is usually considered better.
You can do this easily by passing a reference to the Logger instance you created in within MyProject to your instance of WebServer.
In your WebServer class, you can make a property (a getter/setter) of type Logger, so that WebServer knows about the Logger you want to use. Another alternative (probably better) is to add a parameter to the constructor of WebServer that takes the Logger. Pseudo code for adding the property:
public class WebServer
{
public WebServer(Logger l)
{
Log = l;
}
//relevant code....
public Logger Log { get; set; } //Short hand property declaration, see http://msdn.microsoft.com/en-us/library/bb384054.aspx
}
From inside the WebServer class, you can use the logger like Logger.Add("some text...");
Then, in your Main method, you can pass in your instance of Logger like so:
namespace MyProject
{
class MyProject
{
public static Logger Logs = new Logger();
public static WebServer Web;
static void Main()
{
Web = new WebServer(Logs);
}
}
}