Dependency injection not supported in C# nodes?

I’m getting odd exceptions with C# nodes if I call a method from a custom library which has generic inputs and uses IoC. At least, this is what I’ve managed to narrow it down to.

Unfortunately the exception messages I’m getting are red herrings - in the example below I’m not referencing RevitNodes and to get some more clues, if I provide a more derived type (i.e. concrete implementation of my interface) into the method with the less derived (generic) type I start seeing more useful exceptions (invalid casts) but still no solution. when I debug in Visual Studio it still yields no clues - I just get the same red herring exception message.

The method signature:

This is not a missing library problem - I’m making no calls to RevitNodes.dll!

Isn’t IFaceDataShell<> and FaceDataChain class in RevidNodes.dll being called from your C# scripting component?

As I said, I’m not making calls to RevitNodes, and those named types are not from that library either. IFaceDataShell<> and FaceDataChain are my own interfaces and types coming from my own custom library.

And yeah, I’m instantiating the FaceDataChain from the GH C# node.

The error message should tell you what line the problem is in. If it’s zero, check that you’re not calling it from the using section. Even that other library don’t refer to RevitNodes.dll? In that case, it could be a version problem.

The only dependencies with my library are RevitAPI and RhinoCommon. I’ve added a reference to RevitNodes as last resort, but the exception is the same. I think it is a limitation as the same code works as a custom node in Dynamo.

Last bullet. Had you Copy Local to False? Do you get the same exact error as before?

I wish it was something as simple as that :wink: but for the sake of doubt, I’m not referencing the RevitNodes library anywhere. Its a problem with GH’s C# interpreter from what I cant tell; it can’t resolve the exception and is just defaulting to an erroneous exception which doesn’t provide any insights as to the true cause.

And do you know if the library you use doesn’t reference RevitNodes?

Easy to check: If you remove the lines of code that call your library in the script, the error persists? If not, the problem is in your library for referencing another library without all its dependencies or something like.

Yes of course, it doesn’t reference it - its a Revit Addin using Rhino Inside just to clarify.

Lets inverse the theory: IFaceDataShell is my type and the concrete implementation injects a BrepFace - neither are RevitNodes types nor does RevitNodes have its own types of the same name, so the compiler can easily resolve which library they come from. Also, the breakpoint in Visual Studio would throw an ambiguous type exception if that was the case, but it doesn’t. It doesn’t even get beyond the constructor call which would indicate some problem with the input parameters or the interpreter. The faceDataList is valid, the exception hit when the FaceDataChain constructor is called - and the exact same code works fine in Revit.

I guess you mean by IoC: IoC container or the Service locator pattern? This is not the same! Using events on objects is also IoC, but this is not what you are refering to.

First of all IoC container on libraries is really bad! If you use IoC container you are in need to register the services on application startup or at least use the correct Dependency Injection pattern to make dependencies clear. A library has no common entry point. If there is no clear place of setting up the services and your objects does not expose all dependencies in object member signatures, you are in deep trouble. Could it be that you are not supposed to load this library? Or is this library your library and you did not understand the pattern?

At least you can try to harvest the dependencies by using tools such as NDepend or similar, but it still requires to setup the service, which is another stupid thing to do from a GH Script.

ServiceLocator patterns are twofold, they are useful within your own app, because it allows you to access common things on each object without passing things from object to object, but in case somethings goes wrong you almost always get weirdo runtime errors. Some people call this even an antipattern.

No, just a subset of IoC; dependency injection. We normally use Autofac to create our IoC containers but not in the case above, its just a simple test on a small part of our library. Also its worth mentioning that IoC containers are widely used in professional software development to ensure code is maintainable, highly decoupled, easier to test with a centralised dependency logic. Autofac provides everything you need to do the dependency registrations and set the lifetime of your objects. Anyway, lets agree to disagree on the subject and move on!

What I’m doing really isn’t that complicated, I wrote the library so I know the dependency chain and don’t need to start evaluating the dependency chain with other tools. Also, when I test the same code in Revit it runs fine, which rules out a dependency problem otherwise I would get an exception running in Revit too.

I would probably use fuslogvw to see how/why/when things are being loaded, and hopefully why they are failing. Just guessing, it sounds like there is an exception occurring in a static ctor somewhere.

I do professional software development. And we use IoC Container and Autofarc. But not within a library. IoC Container is not equal to dependency injection, but you use dependency injection to distribute your services managed within the service container. Nevertheless, my point is using a IoC Container in a library is problematic, because someone else using this library has to know what and which services to register. Otherwise you get runtime errors, such as unresolved dependencies etc. This seems to be the problem here. Many people use IoC Container but falsely implement dependency injection, actually hiding dependencies with an IoC Container, whereas di is also about exposing dependencies within the constructor .

It sounds like you’re using Autofac incorrectly; you’re supposed to create your IoC container and resolve the dependencies at the entry point of your application, which avoids any potential for unresolved dependencies or requiring anyone else to know your dependency hierarchy. IoC containers really aren’t a problem when implemented correctly and are absolutely fine when used in libraries too - in fact, any competently architected add-in which is built cross-platform for say, Revit and Rhino, would be utilising an IoC container at its core.

Anyway, I have some breaking news…I think I’ve discovered the culprit - a stack overflow exception triggered by some rouge code in one of the geometry evaluators the FaceDataChain constructor called. Once that was fixed, everything is working fine.

Nevertheless, thanks for all the suggestions :+1:

I’m not using Autofac incorrectly. I’m just using the pattern with caution. If you assume anybody using a 3rd library know’s how to register (not resolve!)- a service correctly for it at startup than you are indeed optimistic. Resolving happens on runtime, this is why it throws runtime errors not compilation errors. And this also why this pattern is controversial. Rhinocommon is not using this sort of pattern at all. Of course there are some Singleton’s. In the end ioc container/ service locator is a modern and csharpish way of working with globals.

If a Stack overflow exception causes such odd behaviour and totally wrong message, then you cannot tell me this is working fine. Anyway I’m out.

The stack overflow was caused by a recursive function not IoC.