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.
Related
I'm using an Azure Durable Function to orchestrate other functions, currently contained in the same project. I want to configure services and logging for those orchestrated functions. How can I do that?
Here is some more detail:
In a "normal" Azure Function I have a Program.cs and a Main method with the following code that sets up the environment for the function execution:
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureLogging(loggingBuilder => { loggingBuilder.SetMinimumLevel(LogLevel.Trace); })... etc. pp.
Using the HostBuilder I can add additional logging providers, add caching services etc. Those services are then injected via the constructor of the Azure Function.
Now in comparison when creating a Durable Function project via the VS Code "Durable Function Orchestration" template there is no Program.cs, no HostBuilder and no constructor. There are just some static methods representing the orchestrator and an orchestrated function.
As there is no out-of-the-box HostBuilder in the "Durable Function Orchestration" template - how does the HostBuilder equivalent look like for Durable Functions? Whats the pattern or convention here? Do I write it myself? Or is there some instance floating around or initialization I can hook into? Or should orchestrated functions be put into separate Azure Function projects where I can make use of the HostBuilder?
Any hints are appreciated.
By default the ILogger instance is injected in your functions, unless you are using DI.All you need to do is Use the ILogger.
[FunctionName("funcname")]
public async static Task RunOrchestrator(
[OrchestrationTrigger] DurableOrchestrationContext context,
ILogger log)
{
log.LogInformation("Starting Orchestration");
}
Check
Incase if you using Dependency injection you should just do the below in your startup builder.Services.AddLogging();
Also check
So the solution is to use a FunctionsStartup class as outlined here. This should make dependency injection work, also for Durable Functions.
For me it did not work immediately though and it took a while to figure out why. What I tried is adding an additional parameter (myService) to the static methods like so:
[FunctionName("DurableFunctionsOrchestrationCSharp1_Hello")]
public static string SayHello([ActivityTrigger] string name, ILogger log, IMyService myService)
{
log.LogInformation($"Saying hello to {name}.");
return $"Hello {name}!";
}
I also added the startup class according to the documentation that is supposed to provide the implementation for IMyService.
This did never work. The error I got is this:
Microsoft.Azure.WebJobs.Host: Error indexing method
'DurableFunctionsOrchestrationCSharp1_Hello'.
Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'myService' to
type IMyService. Make sure the parameter Type is supported by the
binding. If you're using binding extensions (e.g. Azure Storage,
ServiceBus, Timers, etc.) make sure you've called the registration
method for the extension(s) in your startup code (e.g.
builder.AddAzureStorage(), builder.AddServiceBus(),
builder.AddTimers(), etc.).
This error message suggests that it should work while in reality it never does.
The solution was to get rid of the static methods and use classes with constructors. Constructor injection WORKS.
The working class looks like this:
public class Activities
{
IMyService _service;
public Activities(IMyService service)
{
_service = service;
}
[FunctionName("DurableFunctionsOrchestrationCSharp1_Hello")]
public string SayHello([ActivityTrigger] string name, ILogger log)
{
log.LogInformation($"Saying hello to {name} {_service.GetType()}.");
return $"Hello {name}!";
}
}
Note that I moved the function here and made it non-static.
The constructor is properly invoked, given a IMyService instance created by the Startup class and then the function is executed.
The minimal startup class I used for testing looks like this:
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
public interface IMyService
{
}
public class MyService : IMyService
{
}
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddSingleton<IMyService>((s) => {
return new MyService();
});
}
}
}
So dependency injection works for Durable Functions, if you are injecting into constructors.
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.
I'm trying to eliminate a call from my API in which the end-user has to pass Assembly.GetExecutingAssembly() in their code, currently my code looks lke the following.
public static Dictionary<int, Packet> Packets { get; private set; }
public static void Init()
{
Assembly assembly = Assembly.GetExecutingAssembly();
foreach (Type type in assembly.GetTypes())
{
if (type.BaseType == typeof(Packet))
{
...
}
}
}
However this only gets all of the classes that are available in the API.dll, and none of the classes from the project that's using the .dll are included,
However if I pass it as a paramter, like so:
public static void Init(Assembly assembly) {}
it loads everything just fine, is this intended behaviour, is there a way to do this all in the background without the end-user ever having to worry about the Assmebly call?
In a nutshell - I have a .dll file that people will be using in their projects, I want to get all of the classes from the project that's using the .dll, not all of the classes inside of the .dll
Is it as simple as Assembly.GetCallingAssembly?
Why are you limiting your clients to have to define their Packet classes in a single assembly? What if a client wants to implement different types of Packet classes in different assemblies? What if someone wants to do run time code generation and create a Packet class at runtime?
Your API shouldn't care where or how Packet classes are defined. It should just expose an interface that will allow clients to register new Packet types directly. That will give clients the maximum flexibility.
Now it would be appropriate to provider helper methods so that clients could register types from assemblies or from the calling assemblies.
Something like:
class PacketRegistration {
public void RegisterPacketType(Type packetType) {...}
public void RegisterPacketType<TPacket>() where TPacket:Packet {
RegisterPacket(typeof(TPacket));
}
public void RegisterPacketTypesFromAssembly(Assembly assembly) {...}
public void RegisterPacketTypesFromCurrentAssembly() {
RegisterPacketTypesFromAssembly(Assembly.GetCallingAssembly());
}
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);
}
}
}