5

So first of all let me explain what I want to achieve:

I want to create a basic .NET Core console application which is responsible for loading external extensions (more on this later on), listening for events, connection handling etc.

Let's assume this application is a Discord bot or Amazon's Alexa. It is responsible for starting, shutting down, connecting and so on. This core application relies on a external Nuget package.

But you want to provide a system to extend this basic application with a plug n play system. So instead of extending the core Git repository (e.g. on Github) you would create another repository with a .NET Core library which provides a new feature/command/skill. This extending project also adds a dependency to the external Nuget package.

At the end you would have a core application in one repository and multiple repositories extending the core application.

How to develop this?

Let's assume

  • Developer A creates the core application in a repository myApp.core
  • Developer B wants to create a "Hello World" command in a separate repository myApp.extensions.hello-world
  • Developer C wants to create a "Ping" command in a separate repository myApp.extensions.ping

Developer A could work on the application it's easy to test locally.

Developers B and C create libraries for their features. To get their libraries loaded into the application Developer A loads all DLL files from a specific directory "Extensions" during startup.

The external Nuget package searches for every implementation of commands. So those extending projects only provide implementations of new commands. The core application only provides connection handling etc.

But how could Developers B and C test their new features if they want to debug the libraries in their IDEs? They can't run that library so they have to test that library by running the core application.

Things might get tricky if developer A is an employee and developer B and C are just customers creating their own commands but don't have access to the core application.

So how would developers B and C debug their libraries locally then? Are there any better approaches for this? Maybe Docker helps out?

Let me give a real world scenario

Developers A, B and C want to create a Discord bot.

  • Developer A creates a repository for the bot core functionality e.g. connecting against the Discord chat
  • Developer B creates another repository for a simple bot command
  • Developer C creates another repository to listen for new users in a specific guild

All three developers import the Nuget package for the Discord C# library. Developers B and C only create libraries with command/event implementations of interfaces provided by that Nuget library.

B wants to test the command, C wants to test the event listener. Both have to run the bot from the repository of A. Or not?

6
  • Developers B and C would create Unit Test Projects and use them to test their libraries. There is no need for them to use the Core Console application. In addition, instead of using Extensions (I'm assuming you mean SVN extensions), developers B and C could 'release' or 'tag' their assemblies and developer A could update his project with the latest B/C assembles at his convenience.
    – quaabaam
    Commented Nov 24, 2020 at 20:19
  • I don't understand what you mean by "Have no access to the core application". Does this mean that the customers will create the extensions and then give them to you to run them with the core application, while they never see the core application? Why can't they use the core application? Isn't it possible to create 'boostrapping' core application they can use for debugging?
    – Euphoric
    Commented Nov 24, 2020 at 22:31
  • @quaabaam yes, they might just unit test it. But they rely on a valid connection etc. so it would be nice to make use of the core application Commented Nov 25, 2020 at 8:24
  • @Euphoric well for my case they have access to the core repository. But how would you debug the library in your IDE? You have to point to the .exe of the core application right? Commented Nov 25, 2020 at 8:26
  • When does the extension get added to the "main" console? Is it a (pre-compilation) project/nuget reference, or are you expecting your post-compilation console application to dynamically load any extensions you put next to it?
    – Flater
    Commented Nov 25, 2020 at 23:54

4 Answers 4

1

The extension developers can create a "bootstrap" console application next to their library. This bootstrap application could start the core application with the plugin (or plugins).

class Program
{
    static void Main(string[] args)
    {
        var coreApp = new CoreApp();
        coreApp.AddExtension(new MyExtension());
        coreApp.Run();
    }
}

This should be possible if you want to invest time and effort into improving extension developer's experience of creating the extensions.

0

Don't overcomplicate things. Developers B and C can just build themselves a simple CLI/Test Harness who's only purpose is to new up the commands in their libraries and exercise the commands they are responsible for.

3
  • the problem is that those features rely on a connection. So they have to make use of the core application Commented Nov 25, 2020 at 8:13
  • of course you could code it twice, for a test scenario, but this would be redundant code Commented Nov 25, 2020 at 8:22
  • @OlafSvenson, they don't need the core application, they need a connection to send data over. They won't (shouldn't) care what's on the other side. They can mock out whatever connections they need that provide fake responses. A bit of repetition isn't necessarily a bad thing if it lets them confidently test their pieces in isolation
    – Ian Jacobs
    Commented Nov 25, 2020 at 14:54
0

I would argue developers B and C must have some kind of access to the application.

Without the application it will be impossible to do comprehensive testing. Unit tests and/or custom test harnesses are good, but since they are written by the plugin developers they can only test how they think the application works, not how it actually works. Developer A could build and publish a separate test harness that simulates the real application, but there is always the risk that there are differences between the test harness and the real application.

Good API design and documentation can reduce the need for testing with the real application, and may be sufficient for a fairly simple API. But I would not like to release a plugin without at least some testing with the real application.

Exactly how you distribute the application to the plugin developers depends on your needs. Simplest would be to just send the application upon request, or publish the source and let them compile it themselves. More elaborate designs might involve a special license or version that reduces functionality in a way to still allow testing, but make it infeasible to use for production. More details are needed for more concrete suggestions.

An example could be AWS lambda. You download a test harness so you can run your project locally and debug it. Once you are satisfied you can upload it and test it on the real servers, but with much more limited debugging support.

11
  • I updated my question with a real world example :) Commented Nov 25, 2020 at 14:04
  • @Olfa Svenson All three need to run the bot, but B and C do not necessarily need to run it from the repository. A could create a build and give it to B and C. I was asking more about the code sharing policy A have. Can the source for the bot be freely shared? Is the code super-secret and is not permitted to leave a restricted network? perhaps not even in binary form? Somewhere in between?
    – JonasH
    Commented Nov 25, 2020 at 14:48
  • B and C have access to the repository of A :) Commented Nov 25, 2020 at 16:05
  • but what if not? E.g. developing skills for Amazon's Alexa? Commented Nov 25, 2020 at 16:06
  • "but since they are written by the plugin developers they can only test how they think the application works, not how it actually works" The core app developers can logically only test for adherence to the contracts (e.g. interfaces) that the core app exports to its extensions. That's logically inevitable, but more importantly the core app developers never need to care about anything more. The extension developers can still test specific additional behaviors related to their specific implementation. Each developer should only write code for the part that they control/care about.
    – Flater
    Commented Nov 26, 2020 at 0:02
-1

Your core application must expose a public API which can be used by other libraries. Preferably, this API would be a simple or a set of simple interfaces. Library projects can then install your core application as a NuGet package and implement its interface(s).

When you implement the interface(s), you have a dependency from the external libraries to your core application. This way developers B and C can easily develop and debug their libraries.

Developer A, on the other hand, doesn't need to reference the libraries of developers B and C. Developer A can use System.Reflection to find all implementations of the core app's interfaces during runtime.

For example, let's say that your core app has an interface called IInitializer. And let's say that every library needs to implement this interface because that's how your core app will initialize the library before using it.

Developer A now wants to fetch all of the implementations of the IInitializer interface from the core app without having a dependency from the core app to the custom library like so:

private IEnumerable<IInitializer> GetInitializerImplementations(Assembly assembly)
{
    var initializerImplementations = from type in assembly.GetTypes()
                        where type.GetInterfaces().Contains(typeof(IInitializer)) &&
                            type.GetConstructor(Type.EmptyTypes) != null
                        select Activator.CreateInstance(type) as IInitializer;
    return initializerImplementations;
}

Developer A has now just loaded all the implementations of the IInitializer interface during runtime. You can iterate through them and use them as you want.

All that's left is to find the assemblies of the libraries and pass them to the method which I wrote, but you said you already know where to find these assemblies.

Also, have a look at this blog post: https://www.codeproject.com/Articles/744862/Dependency-injection-in-class-libraries.

It's a great post which shows exactly what you need.

2
  • 1
    thanks for your reply. but how would you debug the library? I have to point to the .exe of the core application, no? Commented Nov 24, 2020 at 15:38
  • Library devs can create a console app for testing purposes and you can expose a method in your core app which they could call from their console app. This exposed method would act as an entry point. Commented Nov 25, 2020 at 9:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.