简体   繁体   中英

C# Simple Injector, Can I inject different classes at runtime

Currently I have a Process that all users of a website undergo. (Process covers multiple controllers and views).

I have a request to use the same process overall (but with variations) for a separate type of Customer. Rather than fill my affected controllers with if thens I can see I have one of 2 options.

1) Create variations on the controller (backed by a common abstract class for the common features), and figure out how to call a specific controller based on customer type, or keep the controller structure simple, and pass in a dependency that contains the functionality that will vary.

I am leaning towards the second option, but this means I will need to be able to tell simple injector to register different classes with the same interface, and then, depending on a parameter which won't be known until a customer logs in, instantiate the correct class.

ie (I know this code won't work as is)

//in Simple Injector Initialize
 container.Register<ICustomerProcess, RetailCustomer>(Lifestyle.Scoped);
 container.Register<ICustomerProcess, CommercialCustomer>(Lifestyle.Scoped);

And then, when a Customer is Loaded and Authenticated, then directed to a controller that needs ICustomerProcess, Simple Injector will pass in the appropriate class, RetailCustomer or CommercialCustomer

What I can't see from the Simple Injector documentation is how this achieved. So is it even possible (and if so, can someone explain how as my knowledge of Simple Injector is limited and right now I keep going round in circles!

As I see it, the Proxy pattern is the solution to your problem, since:

  • You don't want the consumer (the controller) to know anything about the existence of multiple implementations.
  • You don't want to introduce an additional interface, like ICustomerProcessStrategy , ICustomerProcessFactory or something similar.

The proxy pattern can help, because it allows creating an implementation of the same abstraction ( ICustomerProcess ) and decide at runtime to which implementation it should forward the call.

Such CustomerProcessProxy might look as follows:

public class CustomerProcessProxy : ICustomerProcess
{
    private readonly ICustomerProcess retail;
    private readonly ICustomerProcess commercial;
    private readonly ICustomerContext context;

    // This constructor requires the two original implementations, and an ICustomerContext.
    // The ICustomerContext allows requesting information about 
    public CustomerProcessProxy(
        RetailCustomer retail, CommercialCustomer commercial, ICustomerContext context)
    {
        this.retail = retail;
        this.commercial = commercial;

        // Important note: in the constructor you should ONLY store incoming dependencies,
        // but never use them. That's why we won't call context.IsRetailCustomer here.
        this.context = context;
    }

    // ICustomerProcess methods
    // Each method will request the ICustomerProcess and forward the call to the
    // returned implementation.
    public object DoSomething(object input) => GetProcess().DoSomething(input);

    // Allows returning the correct ICustomerProcess based on runtime conditions.
    private ICustomerProcess GetProcess()
        => this.context.IsRetailCustomer ? this.retail : this.commercial;
}

Now all you'll have to do is register your CustomerProcessProxy and ICustomerContext implementation and you're done.

container.Register<ICustomerProcess, CustomerProcessProxy>();
container.Register<ICustomerContext, AspNetCustomerContext>();

Obviously you will have to implement an ICustomerContext and how to do this depends on how you would retrieve information about the customer, but I can imagine an implementation for ASP.NET that uses the Session to store whether or not the user is a retail customer. Such implementation might look as follows:

public class AspNetCustomerContext : ICustomerContext
{
    public bool IsRetailCustomer => HttpContext.Current.Session["IsRetail"] != null;
}

That's all you need. Now when a controller calls DoSomething on the injected ICustomerProcess , it ends up calling CustomerProcessProxy , which will dispatch the call to either RetailCustomer or CommercialCustomer .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM