I currently have two "worker services" that invoke an action specified by the caller, while passing a "context" to the action:
public interface ISampleContext { ... }
internal sealed class SampleContext : ISampleContext { ... }
public static class SampleWorker {
public static void Run(Action<ISampleContext> action) =>
action(new SampleContext());
}
Pretty straight forward.
Now, I mentioned I have two of these. Let's call them WorkerA and WorkerB for simplicity, and assume they're both set up the way defined above (keeping in mind they both have dedicated "contexts"). Both of them provide different services, and are often used together:
WorkerA.Run((contextA) => {
WorkerB.Run((contextB) => {
DoSomething();
});
});
Now I have two issues with this:
- I need to add a
WorkerCto this. - This is used a lot.
I'm trying to create a unified "worker service", in an effort to reduce the nested calls as I introduce more "worker services". The concept is that the call would include type arguments to the required "worker services", like so:
GlobalService.Run<WorkerA, WorkerB>((context) => {
DoSomething();
});
That part is easy to accomplish. However, a sharp mind will notice an issue with that setup.
XY Clarifications
It was pointed out in a comment that this question is a perfect example of an XY problem, so I wanted to take the time to break down what I'm currently doing with these "worker services". The three services I will have as of this afternoon are for:
- Analytics
- Exception Logging
- A Common Iterator
The analytics worker service simply records the start and end times of the execution of actions so that I can see where I have delays in my code.
The exception logging worker service catches unhandled exceptions, logs as much information as possible, and re-throws them. This allows me to have insight into where my code has issues that are swallowed upstream for any reason, so that I can repair the code that's broken, and, repair the code that's swallowing the exception in the first place.
The common iterator is for re-attempting calls to SQL or APIs. It's simply a for loop that executes with a delay on failure, until told to break out, or the action returns true, or the maximum number of allowed attempts has been reached.
With this knowledge, you can see how I may have some areas that can use all three services at one time, while other areas may only use one or two.
How do you access the properties and methods from each individual "context", without sending each one through individually?
For example:
public interface IContextA { void PostA(); }
public interface IContextB { void PostB(); }
public interface IContextC { void PostC(); }
...
GlobalService.Run<WorkerA, WorkerB>((context) => {
context.PostA();
context.PostB();
context.PostC(); // This is not valid as WorkerC was not specified.
});
Is there a way to accomplish this in a way that is scalable and easy to maintain, without using dynamic or similar?
By scalable and easy to maintain, I mean that I shouldn't have to create multiple new interfaces and classes when introducing new "worker services".