-
Notifications
You must be signed in to change notification settings - Fork 152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Injecting a class defined inside a NuGet package - possible? #1011
Comments
I'm sorry @wizfromoz, but your question is very broad. Can you provide more details? Please provide realistic code examples. Do these external classes have default constructors, and if not, please show or describe what typical constructors look like. Do these classes share interfaces, or are they all intended to be used independently. What do you mean by "loading on demand" and why do you need that? What lifestyle should these classes get? The more information you provide, the more helpful my answer can be. |
Hi @dotnetjunkie, My exposure to DI comes from Java (J2EE) environment and, I guess, because my only recent exposure to SimpleInjector, I'm trying to figure some key ideas. One thing that bugged me, and this is the reason for my question, is that the application itself has the dependency on classes that implement various interface, because of the need to do all these registrations explictly, In Java frameworks I worked with, the application was free from any references to concrete classes, as this was being resolved by the DI framework, with the help from Java class loader. Ok, in addition to this, you could also annotate certain classes in your application as "beans" and DI would take them ("register") as well. The thing I miss is this complete independence from concrete classes, but I guess this is really impossible in a DI framework where the application itself has to define which concrete classes provide which services before DI can get to work. I guess the good thing is that at least these registrations are all concentrated in well-defined place in the application, |
Keep in mind that the startup part of the application typically references all other assemblies of the application. It does so directly or indirectly via other assemblies. Assembly references are transitive, meaning that if A references B and B references C, A also references C. You can check this by trying to use A while removing C. A won't work, which implies the transitive nature. This means that your startup project already has a dependency on all projects and therefore on all interfaces and concrete types in the application, whether or not you register types explicitly or with some sort of convention doesn't change the existence of this dependency. This holds equally to Java applications and even dynamic languages; this is not unique to .NET. The only way to completely decouple the startup project from logic is by implementing a plugin model, where assemblies can truly be dropped into a folder and the startup project can compile and start without the existence of such assembly. While useful, for most types of applications such plugin model only over complicates things. So even in the Java applications you worked with, the startup project would typically still reference all assemblies. That doesn't mean, though, that it can be useful to automate the process of registering dependencies to a point that you don't have to specify each concrete type explicitly. On the contrary, this process of Auto-Registration is immensely beneficial. As a matter of fact, a DI Container has little benefits over using Pure DI when you only use Explicit Registrations. A DI Container becomes beneficial when you start using Auto-Registrations and Convention over Registration. Mark Seemann even calls a DI Container "pointless" when only Explicit Register is applied. (In the book that I coauthored with Mark, we elaborate on this in chapter 12.)
So the question now becomes, why are you registering all your classes explicitly using Simple Injector, because there is no reason to do this. But even with almost all DI Containers for .NET (and there are many), there are ways to use Auto-Registration.
Simple Injector is not limiting you in any way to do this. However, in contrast to most Java DI Container frameworks, with Simple Injector we choose not to include any attributes for you, because that would cause lower-level application modules to be coupled to the DI library, which would cause vendor lock-in. In a sense it is ironic that many DI containers that exist to allow application code to be written in a loosely coupled fashion, force you to let your classes take a hard dependency on (attributes of) the framework. Another design decision that we made in Simple Injector is to have a limited Auto-Registration API, because "In most cases [it is] easier to write Auto-Registrations using LINQ; a language that most developers are already familiar with." In case you wish to mark your components with an attribute, you will have to define the attribute yourself, and also need to specify the code (likely LINQ or query extension methods) that decides what and how to register classes. For instance: // define in a module that all components depend on
public sealed class ComponentAttribute : Attribute { }
// Inside your Composition Root:
var registrations =
from assembly in AppDomain.CurrentDomain.GetAssemblies()
from component in assembly.GetTypes()
where component.GetCustomAttribute<ComponentAttribute>() != null
from service in component.GetInterfaces()
select (service, component);
foreach (var registration in registrations)
{
container.Register(registration.service, registration.component);
} Whether this exact code will work for you or needs to be extended depends highly on your use case. Simple Injector does have an Auto-Registration API, btw, but it focuses around generic types, because handling generic types using reflection is very complex. The applications I write and help maintain are mainly focused around generic interfaces. Probably 90% to 95% of the components that we write in these applications implement one generic interface. Generic abstractions have many advantages, but one of them is the easy that allows them to be registered. In a sense, the generic interface provides metadata to the DI Container, similar to what attributes would do, but with included compile-time support. For instance, in my applications, most business operations implement the container.Register(typeof(ICommandHandler<>), AppDomain.CurrentDomain.GetAssemblies()); Perhaps you can do something similar in your application. In case these application components implement a generic interface. But a regular (non-generic) interface, can also be used as a marker, similar to what you'd do with an attribute: var registrations =
from assembly in AppDomain.CurrentDomain.GetAssemblies()
from component in assembly.GetTypes()
where typeof(IBusinessOperation).IsAssignableFrom(component)
from service in component.GetInterfaces()
select (service, component); I hope this gives you some ideas. If you have any follow-up questions, please let me know. |
excellent reply, cannot disagree with you on any point. Thank you very much for taking time to answer. This closes the matter for me. |
One additional note, there's also an IEnumerable<Type> types = container.GetTypesToRegister<IBusinessOperation>(
assemblies: AppDomain.CurrentDomain.GetAssemblies());
|
Hi,
we have a following use case: within organization, we have a number of NuGet packages that provide access to company's specialized hardware. These packages are then used in a large, complex application. The front end classes in these packages are accessed via interfaces and the implementation classes hide behind factory methods.
It would be much nicer if it was possible to use DI container to inject instances of these classes. Would I have to manually register classes from NuGet packages or is there a way for DI to load them on demand?
The text was updated successfully, but these errors were encountered: