Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# how to "register" class "plug-ins" into a service class?

Update#2 as of year 2022

All these years have passed and still no good answer. Decided to revive this question.


I'm trying to implement something like the idea I'm trying to show with the following diagram (end of the question).

Everything is coded from the abstract class Base till the DoSomething classes.

My "Service" needs to provide to the consumer "actions" of the type "DoSomethings" that the service has "registered", at this point I am seeing my self as repeating (copy/paste) the following logic on the service class:

public async Task<Obj1<XXXX>> DoSomething1(....params....)
        {
            var action = new DoSomething1(contructParams);
            return await action.Go(....params....);
        }

I would like to know if there is anyway in C# to "register" all the "DoSomething" I want in a different way? Something more dynamic and less "copy/paste" and at the same time provide me the "intellisense" in my consumer class? Somekind of "injecting" a list of accepted "DoSomething" for that service.

Update#1 After reading the sugestion that PanagiotisKanavos said about MEF and checking other options of IoC, I was not able to find exactly what I am looking for.

My objective is to have my Service1 class (and all similar ones) to behave like a DynamicObject but where the accepted methods are defined on its own constructor (where I specify exactly which DoSomethingX I am offering as a method call.

Example: I have several actions (DoSomethingX) as "BuyCar", "SellCar", "ChangeOil", "StartEngine", etc.... Now, I want to create a service "CarService" that only should offer the actions "StartEngine" and "SellCar", while I might have other "Services" with other combination of "actions". I want to define this logic inside the constructor of each service. Then, in the consumer class, I just want to do something like:

var myCarService = new CarService(...paramsX...);
var res1 = myCarService.StartEngine(...paramsY...);
var res2 = myCarService.SellCar(...paramsZ...);

And I want to offer intellisense when I use the "CarService"....

In conclusion: The objective is how to "register" in each Service which methods are provided by him, by giving a list of "DoSomethingX", and automatically offer them as a "method"... I hope I was able to explain my objective/wish.

In other words: I just want to be able to say that my class Service1 is "offering" the actions DoSomething1, DoSomething2 and DoSomething3, but with the minimum lines as possible. Somehow the concept of the use of class attributes, where I could do something similar to this:

// THEORETICAL CODE
[RegisterAction(typeOf(DoSomething1))]
[RegisterAction(typeOf(DoSomething2))]
[RegisterAction(typeOf(DoSomething3))]
public class Service1{
    // NO NEED OF EXTRA LINES....
}

enter image description here

like image 953
Dryadwoods Avatar asked Dec 21 '16 08:12

Dryadwoods


Video Answer


2 Answers

For me, MEF/MAF are really something you might do last in a problem like this. First step is to work out your design. I would do the following:

  1. Implement the decorator design pattern (or a similar structural pattern of your choice). I pick decorator as that looks like what you are going for by suplimenting certain classes with shared functionality that isn't defined in those clases (ie composition seems prefered in your example as opposed to inheritance). See here http://www.dofactory.com/net/decorator-design-pattern

  2. Validate step 1 POC to work out if it would do what you want if it was added as a separate dll (ie by making a different CSProj baked in at build time).

  3. Evaluate whether MEF or MAF is for right for you (depending on how heavy weight you want to go). Compare those against other techniques like microservices (which would philosophically change your current approach).

  4. Implement your choice of hot swapping (MEF is probably the most logical based on the info you have provided).

like image 82
Dessus Avatar answered Oct 19 '22 17:10

Dessus


You could use Reflection. In class Service1 define a list of BaseAction types that you want to provide:

List<Type> providedActions = new List<Type>();
providedActions.Add(typeof(DoSomething1));
providedActions.Add(typeof(DoSomething2));

Then you can write a single DoSomething method which selects the correct BaseAction at run-time:

public async Task<Obj1<XXXX>> DoSomething(string actionName, ....params....)
{
    Type t = providedActions.Find(x => x.Name == actionName);

    if (t != null)
    {
        var action = (BaseAction)Activator.CreateInstance(t);

        return await action.Go(....params....);
    }
    else
        return null;
} 

The drawback is that the Client doesn't know the actions provided by the service unless you don't implement an ad-hoc method like:

public List<string> ProvidedActions()
{
    List<string> lst = new List<string>();
    foreach(Type t in providedActions)
        lst.Add(t.Name);
    return lst;
}
like image 44
Simone Cifani Avatar answered Oct 19 '22 15:10

Simone Cifani