Giter VIP home page Giter VIP logo

Comments (6)

dotnetjunkie avatar dotnetjunkie commented on May 20, 2024 1

There are a few options here. I'll give the two that came to mind first.

First, really try to separate the registration phase from the resolve phase. In my experience this is always possible and preferable. It does make your code much cleaner and understandable, and makes it less prone to weird bugs. In your case for instance, it seems weird to have the main application use some IDriverInfo service that is registered by some other plugin. Since this IDriverInfo contains configuration data, it should be available before the container is registered. If this class contains not only configuration data but also behavior, the class should be split in two; it has two responsibilities. If this data must come from a plugin, it's much cleaner to use a different communication mechanism to get this configuration data from the plugin. Don't abuse the container for this. The container is not a dictionary of configuration values to pass around. As a matter of fact, you should not have any abstractions such as IApplicationConfiguration that just contains properties with configuration values for other parts of the application to use. This violates SOLID and leads to ugly code (I speak from experience here, I used such abstraction for a long time). Instead, load configuration values before configuring the container and inject a specific configuration value into a service that needs it. For instance, don't inject an IApplicationConfiguration into a UnitOfWork class, but let that UnitOfWork take a string connectionString argument in its constructor. If you find yourself having to inject the same configuration value into multiple components, you are missing an abstraction to hide that configuration value behind. For the connection string for instance, this could be an IConnectionFactory which exposes an SqlConnection CreateAndOpenConnection() method. This hides the configuration value from the application and the only component that needs to be injected with this connection string is the SqlConnectionFactory component.

Another option is to use a proxy class to allow postponing the decision which component to use. This is especially useful when the types can/should change at runtime, but it can also used for configuration values, in case the refactoring that I described above is too much work. Here's what the proxy might look in your case:

public class RuntimeSelectableFlashlightDriverProxy :  IFlashlightDriver {
    private readonly Func<IFlashlightDriver> selector;  
    public RuntimeSelectableFlashlightDriverProxy(Func<IFlashlightDriver> selector) {
        this.selector = selector;
    }

    void IFlashlightDriver.Flash() {
        this.selector.Invoke().Flash();
    }
}

This class can be registered as follows:

container.RegisterSingle<IFlashlightDriver>(
    new RuntimeSelectableFlashlightDriverProxy(() =>
        container.GetInstance<IDriverInfo>().Version == 1
            ? container.GetInstance<FlashlightDriverV1>()
            : container.GetInstance<FlashlightDriverV2>()));

from simpleinjector.

dotnetjunkie avatar dotnetjunkie commented on May 20, 2024

Unfortunately, the given example is too generic to allow any specific feedback to be given. What does IService1 and Service1 represent, and why does it have a Version property, and how is this version result determined? Please be as specific as possible about your use case and design (more code is better).

from simpleinjector.

prasannavl avatar prasannavl commented on May 20, 2024

I had actually intended it to be generic. Version property was just a hypothetical generic representation of the notion that a previously registered service during the startup phase has to be resolved, and based on a few conditions, further services be registered.

One simple real example in one of my projects involves an IFlashlightDriver which resolves to different services based on a IDriverInfo service, which ends up being registered by a different Plugin in the previous stages of the bootstrapper. And the flashlight is registered by another disconnected stage in the bootstrapper.

from simpleinjector.

prasannavl avatar prasannavl commented on May 20, 2024

Thank you for the advice. I think I'll take the second approach, since fortunately, all the services that are in question in my scenario already are served via proxy since they are runtime swappable services. Your advise solves my current requirement. I appreciate your time for the detailed reply.

It however does concern me a little, that SI doesn't have a builder like mechanism, that I currently use to solve multi-staged bootstrapping like these, which is provided by DI containers like Autofac, Grace, or the alternate route taken my LightInject and such where you're free to register anytime directly with the container itself. With SI, it seems difficult to compose services from various parts of the application, unless its all done in one sweep. Perhaps, you're right that I'm abusing the container by trying to make the container serve nearly everything. But basically this is the process

ApplicationCore:
=> PreInitialize();
=> RegisterServices(Container c);
=> Initialize();

PlugIn1:
=> PreInitialize();
=> RegisterServices(Container c);
=> Initialize() // Can use the registered services to perform initialization
//, or any other service of ApplicationCore, or previous plugins.

PlugIn2:
-- Same as above.

And so on.

I take it, this not a pattern that is friendly to SI, if I use one container which handles all of these.

from simpleinjector.

dotnetjunkie avatar dotnetjunkie commented on May 20, 2024

I'm not sure that SI is unfriendly in this case. It might be annoying that Simple Injector forces you to change your design, but this limitation is there for a reason. The risks of doing registrations like this however are very real, so for me that was reason enough to not allow this. Don't forget that Autofac's ContainerBuilder is exactly meant to do what Simple Injector forces you: to separate the registration phase from the resolve phase.

What you can do is split your initialization phase in two steps, where you first iterate over all plugins and do some sort of pre-initialization and build up the configuration values, and after that iterate again where all plugins can register (but not resolve).

I do agree though that the documentation is a bit limited in this case, and doesn’t really describe how to make this work in Simple Injector. Main reason for this is that it’s impossible to come up with a general scenario that works for everybody. The solution is highly dependent on the exact use case. This is where the community comes in to help you and supply feedback to the design at hand.

But if you feel really strongly about being able to do this, and you feel that Simple Injector is limiting you in this, and you are willing to take the risk of making accidental misconfigurations, do switch to a different library. Simple Injector isn't this Swiss army knife that works in all scenarios. I feel strongly about protecting our users from doing what I see as the wrong thing, but if you don't share this opinion or vision, there are other libraries that might better suit your needs. I won’t be offended if you switch :-).

from simpleinjector.

prasannavl avatar prasannavl commented on May 20, 2024

Fair enough. Thanks for your time and help with this. I'll see if I can make it work by shuffling the design a bit, or go probably go back to Autofac as a last resort.

from simpleinjector.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.