Allow our app to call a customer-provided code that calls our code

33 views Asked by At

I am looking for the simplest solution to allow our .Net EXE to call a DLL provided by a 3rd party. Here's the rub - that DLL calls into objects in our code.

One of the nice things in .Net is that an EXE can be used as an assembly in other project, like it's a DLL. My original idea was for them to import our EXE into their project and then call functions within.

I mocked that up and it works great, but the customer doesn't want it to work that way, they want to run our app and have it call their code, ideally provided in a DLL.

Now this would be trivial to do if their code didn't call ours, but in my attempts to make a project mocking this up I always run into circular reference issues. I know I can avoid all that with runtime support, but perhaps I am missing a simpler solution?

3

There are 3 answers

1
K. Berger On

Extract the components that your customers need to interact with into their own assembly. This might even be just a single interface (let's call it IPluginfor the sake of conciseness, although I don't know whether your customers really write plugins).

public interface IPlugin
{
  void Register();
  void Execute();
}

Now your customers implement this interface with a class in their DLL and copy that to your application's dir (in the easiest case).

Your application, might do the following on startup.

  • Load assemblies in the specified path (use Assemblyclass).
  • Get types in the assemblies, which implement your interface
  • instantiate and call the Register-method when needed

when registering, the plugins could subscribe to events on your application, or your app could call their execute-method when needed.

This might require some additional work or even rework, but it's easily maintainable and simple. Depending on what you want to do, you can pass other classes/interfaces to the plugins when calling the methods on the interface. Imagine a class Application for instance, which implements an interface IApplication, which is defined in the interface-dll. Now the plugins can call methods in your application, store a reference to it and do whatever you allow them to do in your interface definition.

I hope this is better to understand than my first comment :-)

Cheers

0
Matías Fidemraizer On

You can easily overcome this situation by using reflection and avoid compile-time references between your application and third-party developments:

  1. Define a location where third-party assemblies should be placed.
  2. Look for assemblies on the location defined in #1 and load them during run-time.
  3. You can define some interface that should implement these third-party assemblies to set an assembly entry point.
  4. Locate an implementation of the interface defined in #3 and call the entry point method (for example: impl.Main()).
  5. Done! The whole third-party assembly has been loaded into your application and it'll be able to interact with whatever you've injected into the #3 interface implementation.

Code sample:

IService service = new ServiceImpl();

IEnumerable<IAddin> addIns = Directory.GetFiles("<path to third-party assemblies>")
         .SelectMany(file => Assembly.Load(file).GetTypes())
         .Where(type => typeof(IAddin).IsAssignableFrom(type))
         .Select(type => (IAddIn)Activator.CreateInstance(type, new object[] { serviceImpl });

foreach(IAddin addIn in addIns)
{
     addIn.Main();
}

And some add-in might looks as follows:

public interface IAddIn
{
    void Main();
}

public class SomeAddIn : IAddIn
{
    public SomeAddIn(IService service)
    {
         Service = service;
    }

    private IService Service { get; }

    public void Main()
    {
        // Service will contain whatever you need to interact with the 
        // main app
    }
}

Don't re-invent the wheel

Note that I've provided you a code sample to let you understand the approach, although I wouldn't go this way from scratch.

I would look forward for:

0
Alain T. On

I don't know much about .Net but it seems to me that your circular references are an architectural issue more than a coding problem. I'm sure there are several recipes in .Net to achieve dependency inversion or injection.

You need to design an intermediate "class" that will encapsulate their functionality and completely hide their internal implementation. They should provide you with an instance of that class either at startup time or as a parameter when they call your code. This would be like the "Delegate" approach that I'm sure you worked with in iOS/MacOS/Swift.

There could even be several different delegate classes if there are many usage contextes.

You should take charge of designing the class's methods and properties (its signature) and they should be responsible for instantiating the object and implementing the code behind it.