3

As you know, Ninject binds all interfaces to implementations in the composition root. We have a class which is dependent on an external dll, but we don't want to deploy it (the physical dll file in the bin directory) if it's not being used. The class ExampleClass is stored in our Framework project, this means it's referenced by any application we are building. And that's why we are getting the following when deploying any of our application:

Could not load file or assembly ExternalDll, Version=x.x.x.xxx, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxx

Is there any workaround, like encapsulating the implementation into another class or something? Or runtime initialisation (Factory pattern). Which of these will solve my problem? I also tried Lazy binding but without success.

Constructor injection of implementation where the external dll is being used

namespace Framework
{
    public class ExampleClass
    {
        private readonly IUsesExternalDll _usesExternalDll;

        public ExampleClass(IUsesExternalDll usesExternalDll)
        {
            _usesExternalDll = usesExternalDll;
        }
    }
}

Interface

public interface IUsesExternalDll
{

}

Implementation

using externalDll; //this using is a reference to external dll

public class UsesExternalDll : IUsesExternalDll
{

}

Binding

kernel.Bind<IUsesExternalDll>().To<UsesExternalDll>().InTransientScope();
Jan Muncinsky
  • 4,282
  • 4
  • 22
  • 40
Ozkan
  • 3,880
  • 9
  • 47
  • 78
  • 1
    Perhaps I'm not getting the point, but is `IUsesExternalDll` in the same assembly as `UsesExternalDll`? If so, you can separate the interface into a common assembly. The interface assembly will always need to be deployed alongside all your apps, but you should be able to omit assembly with concrete class `UsesExternalDll` provided of course you don't try and bootstrap it. – StuartLC May 30 '18 at 11:57
  • Interface and implementations in this example are in the same assembly. What do you mean with bootstrapping it? If I remove the Ninject Binding, it says it can't be resolved. And if the binding is added, it is expecting the .dll file also... And with assembly, you mean the project assembly, not the namespace right? – Ozkan May 30 '18 at 12:02
  • @StuartLC Great article I've read a minute ago: https://www.blinkingcaret.com/2016/02/03/dependency-injection-without-referencing-implementations/. Our software is unfortunately not separated by assembly. I know it's bad design.... But isn't there a quick-fix to solve my above problem? But still there is a place where I'm binding the interface to the implementation and the implemation is being initialised which will look for the dll. So this means, the dll must always be present? – Ozkan May 30 '18 at 12:13
  • I've just built a sample, and no, `ExternalDll` will always be dragged along to the final app, even if none of the classes / methods in `ExternalDll` are actually used by it (and if you separate the interfaces into `IExternalDll` and couple `Framework` via interface only, then it's fine). If `ExternalDll` contains sensitive code, you might try and create a spoofed dll (provided it doesn't have a [strong name](https://stackoverflow.com/q/13346265)), which should appease Fusion. But I think longer term you may be looking for a "Plugin" pattern approach to managing the DLL. – StuartLC May 30 '18 at 12:32

2 Answers2

0

You can replace your binding with convention binding:

kernel.Bind(x => x.FromAssembliesMatching("AssemblyWhereUsesExternalDllIsLocated.dll")
    .SelectAllClasses()
    .BindAllInterfaces()
    .Configure(c => c.InTransientScope()));

Now types inside are bound only when this assembly is found.

Jan Muncinsky
  • 4,282
  • 4
  • 22
  • 40
  • How can I only bind IUsesExternalDll instead of all? And btw `AssemblyWhereUsesExternalDllIsLocated.dll` will always be present. So that's not a solution for my problem I think. – Ozkan May 31 '18 at 08:17
0

How I solved this:

1) Move the Ninject binding to the specific application where it is used (higher level).

2) Make the constructor injected parameter Optional.

namespace Framework
{
    public class ExampleClass
    {
        private readonly IUsesExternalDll _usesExternalDll;

        public ExampleClass([Optional] IUsesExternalDll usesExternalDll)
        {
            _usesExternalDll = usesExternalDll;
        }
    }
}

Optional Attribute will lead to only being instantiated if there is a binding. And since I moved the bindings to the specific application where it is being used, in all other applications I won't get the Could not load file or assembly error because UsesExternalDll is not instantiated.

Ozkan
  • 3,880
  • 9
  • 47
  • 78